{
    "openapi": "3.1.0",
    "info": {
        "title": "Laravel",
        "version": "0.0.1",
        "description": "## Public API (X-API-KEY)\n\nПрограммный доступ к сервису по API-ключу — **отдельный surface**, не равный фронтовому\nJWT-API. Аутентификация: заголовок `X-API-KEY`. Каждый эндпоинт требует соответствующего\n**права ключа** (`orders.read/write/download`, `templates.read/write`, `voices.read/write`,\n`billing.read`, `profile.read`, `reseller.read/write`; ключ с `*` проходит любую проверку,\nкроме прав реселлера).\n\n**Права реселлера.** `reseller.read` — профиль и баланс реселлера, статистика, настройки\nкодов, список и показ кодов с ПОЛНЫМИ значениями; `reseller.write` — выпуск и отмена кодов.\nЭти права не входят в пресеты ключа, не покрываются `*` (только явным перечислением) и видны\nв форме ключа только реселлеру; пользователь\nбез профиля реселлера получает на этих эндпоинтах `404` (`not_reseller`).\n\n**Сервисы озвучки:** `elevenlabs` (модели от `eleven_multilingual_v2` до `eleven_v4`),\n`heygen`, `cartesia` (модели `sonic-3.6`, `sonic-3.5`, `sonic-3`) и `fishaudio`. Сервис и модель задаются\nв шаблоне (`POST /templates`), голос — `tts_settings.voice_id`. Свой голос для Cartesia\nклонируется по API: `POST /voices/cartesia/clones` (право `voices.write`), готовый клон\nотдаёт `voice_id`. Голос, склонированный вне Lumean, тоже принимается — по его id у\nCartesia (UUID). Fish Audio — так же: каталог `GET /voices/fishaudio`, клоны\n`POST /voices/fishaudio/clones` (подписка нужна, только если сервис закрыт для оплаты с баланса; без неё — 2 клона; id голоса —\nстрока из 32 hex-символов, не UUID).\n\nВсе ответы — в формате `{ success, message, data, errors? }`. Бизнес-отказ несёт машинный\nкод под двумя ключами с одинаковым значением — `reason` и `error_code` (например,\n`insufficient_balance`, `input_expired`); ветвитесь по коду, а не по тексту `message`.\n\n**Лимиты ключа (опциональны):** rate-limit запросов (`429` с `reason: \"rate_limit_exceeded\"` +\n`Retry-After`) и токен-квота (`429` с телом `{ reason: \"token_quota_exceeded\", error_code, window,\nlimit, used, reset_at, retry_after }`; у пула без сброса по времени `window: \"quota\"`,\n`reset_at`/`retry_after` — `null` и без `Retry-After`).\nКлюч без лимитов работает без ограничений.\n\n**Недоступно по ключу:** пополнения/депозиты, переводы, покупка подписок, мутация профиля,\nуправление самими API-ключами.\n\n---\n\n## WebSocket: real-time события заказов\n\nПрогресс выполнения заказов можно получать в реальном времени по WebSocket. Протокол —\nPusher-совместимый, поэтому подходят стандартные клиентские библиотеки: **pusher-js** или\n**Laravel Echo**.\n\n### Параметры подключения\n\n| Параметр | Значение |\n| --- | --- |\n| Хост | `ws.lumean.app` |\n| Порт | `443` |\n| Схема | `wss` (TLS обязателен) |\n| App key | `7c4dd881d8116ac76fe89aff71d8c2ff` |\n| Auth-эндпоинт | `POST https://api.lumean.app/api/public/broadcasting/auth` |\n\n> **App key** — публичный ключ приложения, не секрет. Подставьте значение `7c4dd881d8116ac76fe89aff71d8c2ff`\n> в конфигурацию клиента, как в примерах ниже.\n\n### Подключение через pusher-js (рекомендуется)\n\n```js\nimport Pusher from 'pusher-js';\n\nconst pusher = new Pusher('7c4dd881d8116ac76fe89aff71d8c2ff', {\n  wsHost: 'ws.lumean.app',\n  wsPort: 443,\n  wssPort: 443,\n  forceTLS: true,\n  enabledTransports: ['ws', 'wss'],\n  cluster: '', // не используется, но поле требуется pusher-js\n  channelAuthorization: {\n    endpoint: 'https://api.lumean.app/api/public/broadcasting/auth',\n    headers: { 'X-API-KEY': 'ВАШ_API_КЛЮЧ' },\n  },\n});\n\n// Все заказы вашего аккаунта ({userId} — числовой ID владельца ключа):\nconst channel = pusher.subscribe('private-orders.42');\n\n// В pusher-js имена событий — БЕЗ ведущей точки:\nchannel.bind('status.changed', (data) => {\n  console.log(data.new_status, data.order.id);\n});\nchannel.bind('progress', (data) => {\n  console.log(`${data.completed}/${data.total} (${data.percent}%)`);\n});\n```\n\n### Подключение через Laravel Echo (опция)\n\n```js\nimport Echo from 'laravel-echo';\nimport Pusher from 'pusher-js';\nwindow.Pusher = Pusher;\n\nconst echo = new Echo({\n  broadcaster: 'pusher',\n  key: '7c4dd881d8116ac76fe89aff71d8c2ff',\n  wsHost: 'ws.lumean.app',\n  wsPort: 443,\n  wssPort: 443,\n  forceTLS: true,\n  enabledTransports: ['ws', 'wss'],\n  cluster: '',\n  authEndpoint: 'https://api.lumean.app/api/public/broadcasting/auth',\n  auth: { headers: { 'X-API-KEY': 'ВАШ_API_КЛЮЧ' } },\n});\n\n// В Echo имена событий — С ведущей точкой:\necho.private('orders.42')\n  .listen('.status.changed', (e) => console.log(e.new_status));\n```\n\n> **Имена событий.** В Laravel Echo слушайте с ведущей точкой — `.listen('.status.changed')`.\n> В pusher-js — без точки — `channel.bind('status.changed', …)`. В таблицах ниже имена даны\n> в Echo-нотации (`.created`); для pusher-js отбрасывайте ведущую точку.\n\n### Авторизация подписки\n\nПриватные каналы (`private-*`) требуют авторизации. Клиентская библиотека делает это\nавтоматически: при подписке она шлёт `POST /api/public/broadcasting/auth` с заголовком\n`X-API-KEY`. Право ключа зависит от канала: каналы заказов и превью чанков —\n**`orders.read`**, клоны голоса Cartesia и Fish Audio — **`voices.read`**. В ответ приходит непрозрачный\nauth-токен, который библиотека подставляет сама — вручную его разбирать или формировать\nне нужно.\n\n### Доступные каналы\n\n| Канал | Назначение |\n| --- | --- |\n| `private-orders.{userId}` | Поток событий по **всем** заказам аккаунта. `{userId}` — числовой ID владельца ключа. |\n| `private-order.task.{orderId}` | Поток событий **одного** заказа. `{orderId}` — UUID заказа. |\n| `private-chunks.preview.{userId}` | Результат фонового расчёта превью разбиения на чанки (`POST /orders/chunks/preview`). |\n| `private-cartesia-voice-clones.{userId}` | Ход клонирования голосов Cartesia (`POST /voices/cartesia/clones`). Право `voices.read`. |\n| `private-fishaudio-voice-clones.{userId}` | Ход клонирования голосов Fish Audio (`POST /voices/fishaudio/clones`). Право `voices.read`. |\n\nПодписаться можно только на свои каналы. Чужой канал → `403`.\n\n### События и payload\n\n**Канал `private-orders.{userId}`**\n\n| Событие | Когда приходит | Форма payload |\n| --- | --- | --- |\n| `.created` | Создан новый заказ | `{ order: {...} }` |\n| `.status.changed` | Сменился статус заказа | `{ order: {...}, previous_status, new_status }` |\n| `.item.status.changed` | Сменился статус элемента (чанка) заказа | `{ item: {...}, previous_status, new_status }` |\n| `.progress` | Обновился агрегированный прогресс по заказу | `{ order_id, completed, total, percent }` |\n\n**Канал `private-order.task.{orderId}`**\n\n| Событие | Когда приходит | Форма payload |\n| --- | --- | --- |\n| `.status.changed` | Сменился статус заказа | `{ order: {...}, previous_status, new_status }` |\n| `.item.status.changed` | Сменился статус элемента (чанка) заказа | `{ item: {...}, previous_status, new_status }` |\n| `.progress` | Live-прогресс выполнения заказа | `{ task_id, progress, chunks_done, chunks_total, timestamp }` |\n\n**Канал `private-cartesia-voice-clones.{userId}`**\n\n| Событие | Когда приходит | Форма payload |\n| --- | --- | --- |\n| `.voice_clone.updated` | Клон создан или сменил статус (`pending` → `cloning` → `ready`/`failed`, `archived`) | `{ voice_clone: { id, provider, display_name, language, description, status, voice_id, attempts, error, created_at, completed_at, failed_at } }` |\n\n`voice_id` клона появляется только в статусе `ready` — его кладут в `tts_settings.voice_id`\nшаблона Cartesia.\n\n**Канал `private-fishaudio-voice-clones.{userId}`**\n\n| Событие | Когда приходит | Форма payload |\n| --- | --- | --- |\n| `.voice_clone.updated` | Клон создан или сменил статус (`pending` → `cloning` → `ready`/`failed`, `archived`) | `{ voice_clone: { id, provider, display_name, language, description, status, voice_id, attempts, error, created_at, completed_at, failed_at } }` |\n\n`voice_id` клона Fish Audio (32 hex-символа, не UUID) появляется только в статусе `ready` — его\nкладут в `tts_settings.voice_id` шаблона Fish Audio.\n\n> **Внимание:** событие `.progress` есть на обоих каналах, но с **разной формой payload**.\n> На `private-orders.{userId}` это агрегат с полем `percent` (0..100). На\n> `private-order.task.{orderId}` — live-прогресс с долей `progress` (0..1) и счётчиками\n> чанков. Различайте их по каналу, на котором подписаны; здесь `task_id` равен `orderId`.\n\nОбъект `order` приходит в облегчённой форме: `id`, `user_id`, `status`, `task_type`,\n`price_units`, `template`, `items[]`, `created_at`, `updated_at`. Объект `item` (чанк):\n`id`, `order_id`, `status`, `chunk_index`, `price_units`, `consumed_units`, `can_retry`,\n`can_regenerate`, `result_file`. Файлы в payload — это только ПУТИ, не содержимое:\n`result.files[]` и `result.service_files[]` — плоские массивы строк-путей, `result_file`\nчанка — строка-путь. Сами файлы скачиваются через бэкенд (`POST /storage/url`, право\nключа `orders.download`).\n\n### Обработка ошибок авторизации подписки\n\n| Код | Причина | Что делать |\n| --- | --- | --- |\n| `401` | Отсутствует или невалиден `X-API-KEY` | Проверьте заголовок и значение ключа. |\n| `403` | Нет права, которого требует канал, либо запрошен чужой канал | Каналы заказов и превью чанков требуют `orders.read`, каналы клонов голоса Cartesia и Fish Audio — `voices.read`; подписывайтесь только на свои каналы. |\n| `429` | Превышен лимит (1200 запросов в минуту к auth-эндпоинту) | Снизьте частоту переподписок; используйте общий канал `private-orders.{userId}` вместо множества `private-order.task.{orderId}`. |"
    },
    "servers": [
        {
            "url": "https://api.lumean.app/api/public",
            "description": "Public API"
        }
    ],
    "tags": [
        {
            "name": "Public Orders"
        },
        {
            "name": "Public Storage"
        },
        {
            "name": "Public Templates"
        },
        {
            "name": "Public Voices"
        },
        {
            "name": "Public External Voices"
        },
        {
            "name": "Public Cartesia Voices"
        },
        {
            "name": "Public Fish Audio Voices"
        },
        {
            "name": "Public Billing"
        },
        {
            "name": "Public Subscriptions"
        },
        {
            "name": "Public Profile"
        },
        {
            "name": "Public AI Models"
        },
        {
            "name": "Public Reseller"
        }
    ],
    "security": [
        {
            "X-API-KEY": []
        }
    ],
    "paths": {
        "/ai/models": {
            "get": {
                "operationId": "aiModel.index",
                "description": "Модели AI API (`https://api.lumean.app/v1`) и их **текущие** цены в LUMC.\n\n**Цены меняются** — поставщики дорожают и дешевеют, модель может временно\nпропасть. Проверяйте цену перед дорогой генерацией и сравнивайте со своим\nпорогом; заранее зафиксировать её нельзя.\n\n- `price.<класс>.per_mtok_minor` — за 1M токенов (`input`, `cached_input`,\n  `output`, `reasoning`, `cache_write`); `price.request.per_call_minor` — за вызов\n  (картинки). Реальное значение — `minor / 10^asset.precision`, готовое — `*_formatted`.\n- `*_display` — та же ставка в валюте `display_currency` (заголовок `X-Display-Currency`,\n  иначе валюта аккаунта), справочно; `null` — курса нет. Списание — в LUMC.\n- `price: null` или `billing_available: false` — модель сейчас платно не вызвать.\n- `version` и заголовок `ETag` меняются при любом изменении цен. Повторный\n  запрос с `If-None-Match: <ETag>` отвечает **304** без тела, пока цены прежние.\n\nСписание идёт по цене на момент запроса; от неожиданного роста трат защищает\nмесячный лимит ключа (`ai_monthly_limit_minor`).\n\n**Права ключа не нужны** — подходит любой активный ключ.",
                "summary": "Current AI model prices",
                "tags": [
                    "Public AI Models"
                ],
                "responses": {
                    "200": {
                        "description": "Модели с текущими ценами",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "version": {
                                                    "type": "string"
                                                },
                                                "models": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/AiGatewayCatalogModel"
                                                    }
                                                },
                                                "asset": {
                                                    "$ref": "#/components/schemas/Asset"
                                                },
                                                "display_currency": {
                                                    "type": "object",
                                                    "properties": {
                                                        "code": {
                                                            "type": "string"
                                                        },
                                                        "symbol": {
                                                            "type": [
                                                                "string",
                                                                "null"
                                                            ]
                                                        },
                                                        "precision": {
                                                            "type": "integer"
                                                        },
                                                        "available": {
                                                            "type": "boolean"
                                                        }
                                                    },
                                                    "required": [
                                                        "code",
                                                        "symbol",
                                                        "precision",
                                                        "available"
                                                    ]
                                                }
                                            },
                                            "required": [
                                                "version",
                                                "models",
                                                "asset",
                                                "display_currency"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "version": "string",
                                            "models": [
                                                {
                                                    "key": "string",
                                                    "name": "string",
                                                    "family": "string",
                                                    "kind": "string",
                                                    "description": "string",
                                                    "context_window": "string",
                                                    "max_output_tokens": "string",
                                                    "capabilities": "string",
                                                    "pricing_mode": "string",
                                                    "price": {
                                                        "key": {
                                                            "per_mtok_minor": 1,
                                                            "per_mtok_formatted": 1
                                                        }
                                                    },
                                                    "billing_available": true,
                                                    "byok": "string"
                                                }
                                            ],
                                            "asset": {
                                                "id": 1,
                                                "code": "string",
                                                "kind": "money",
                                                "precision": 1,
                                                "name": null,
                                                "is_active": true,
                                                "is_default": true,
                                                "default_balance": 1,
                                                "deleted_at": "2026-07-17T12:34:56+00:00",
                                                "created_at": "2026-07-17T12:34:56+00:00",
                                                "updated_at": "2026-07-17T12:34:56+00:00"
                                            },
                                            "display_currency": {
                                                "code": "string",
                                                "symbol": "string",
                                                "precision": 1,
                                                "available": true
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "304": {
                        "description": "Цены не изменились с переданного ETag",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "string",
                                    "example": "string"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "API-ключ отсутствует, неизвестен, неактивен или истёк",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/ai/models/{key}": {
            "get": {
                "operationId": "aiModel.show",
                "description": "Текущая цена одной модели по её `key` (то же имя, что в поле `model` запроса).\nФорма ответа та же, что у списка, — в `models` одна запись. `ETag` и\n`If-None-Match` работают так же. Модели нет или её сейчас нельзя вызвать —\n**404**: не отправляйте генерацию, пока она не вернётся.\n\n**Права ключа не нужны.**",
                "summary": "Current price of one AI model",
                "tags": [
                    "Public AI Models"
                ],
                "parameters": [
                    {
                        "name": "key",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Модель с текущей ценой",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "version": {
                                                    "type": "string"
                                                },
                                                "models": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/AiGatewayCatalogModel"
                                                    }
                                                },
                                                "asset": {
                                                    "$ref": "#/components/schemas/Asset"
                                                }
                                            },
                                            "required": [
                                                "version",
                                                "models",
                                                "asset"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "version": "string",
                                            "models": [
                                                {
                                                    "key": "string",
                                                    "name": "string",
                                                    "family": "string",
                                                    "kind": "string",
                                                    "description": "string",
                                                    "context_window": "string",
                                                    "max_output_tokens": "string",
                                                    "capabilities": "string",
                                                    "pricing_mode": "string",
                                                    "price": {
                                                        "key": {
                                                            "per_mtok_minor": 1,
                                                            "per_mtok_formatted": 1
                                                        }
                                                    },
                                                    "billing_available": true,
                                                    "byok": "string"
                                                }
                                            ],
                                            "asset": {
                                                "id": 1,
                                                "code": "string",
                                                "kind": "money",
                                                "precision": 1,
                                                "name": null,
                                                "is_active": true,
                                                "is_default": true,
                                                "default_balance": 1,
                                                "deleted_at": "2026-07-17T12:34:56+00:00",
                                                "created_at": "2026-07-17T12:34:56+00:00",
                                                "updated_at": "2026-07-17T12:34:56+00:00"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "304": {
                        "description": "Цена не изменилась с переданного ETag",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "string",
                                    "example": "string"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Модели нет в каталоге",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/usage": {
            "get": {
                "operationId": "billing.usage",
                "description": "Сводка по всем лимитам владельца ключа: сколько израсходовано, каков потолок по\nтекущей подписке, тип лимита и когда счётчик обнулится.\n\nЗдесь же видно, какие сервисы вам вообще доступны — набор определяется возможностями\n(entitlements) активной подписки.\n\nДва разных типа лимита не путайте: периодическая квота (сбрасывается по `reset_at`)\nи слоты одновременных задач (освобождаются, как только ваши текущие заказы\nзавершатся). Исчерпание первой ждёт конца периода, второй — минуты.\n\n**Требуемое право ключа:** `billing.read`.",
                "summary": "Get Usage Limits",
                "tags": [
                    "Public Billing"
                ],
                "responses": {
                    "200": {
                        "description": "Список лимитов использования",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "limit_id": {
                                                        "type": "integer",
                                                        "description": "Идентификатор записи лимита."
                                                    },
                                                    "service_id": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "ID сервиса, к которому относится лимит. `null` у лимитов, не привязанных к сервису."
                                                    },
                                                    "service_code": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Код сервиса — то самое значение для пути `GET /usage/{service}` и для `service_key` шаблона."
                                                    },
                                                    "service_name": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Название сервиса для показа, на языке аккаунта."
                                                    },
                                                    "entitlement_id": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "ID возможности подписки, которая выдала этот лимит."
                                                    },
                                                    "entitlement_code": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Код возможности подписки, выдавшей лимит."
                                                    },
                                                    "limit_type": {
                                                        "type": "string",
                                                        "description": "Что именно ограничивается. Две принципиально разные вещи:\n\n- **периодическая квота** — расход за период, обнуляется по `period_end`;\n- **слоты одновременных задач** — освобождаются сами, как только ваши\n  текущие заказы завершатся.\n\nИсчерпание первой ждёт конца периода, второй — минут."
                                                    },
                                                    "limit_value": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "Потолок по текущей подписке. `null` — потолка нет (см. `token_unlimited`)."
                                                    },
                                                    "used": {
                                                        "type": "integer",
                                                        "description": "Сколько израсходовано в текущем периоде."
                                                    },
                                                    "remaining": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "Сколько осталось. `null` при безлимите."
                                                    },
                                                    "period_start": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Начало текущего периода учёта, ISO 8601. `null`, если учёт в текущем периоде ещё не открыт (`used` при этом 0): лимит не трогали вовсе либо тратили только в прошлых периодах — период открывается первым расходом, а не по календарю."
                                                    },
                                                    "period_end": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Конец текущего периода учёта, ISO 8601 — в этот момент счётчик обнулится. `null` там же, где `period_start` (учёт в текущем периоде ещё не открыт), и у лимитов без периода (`concurrent`, `total`)."
                                                    },
                                                    "resets_in": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Сколько осталось до сброса счётчика, в человекочитаемом виде. `null` там же, где `period_end`."
                                                    },
                                                    "token_unlimited": {
                                                        "type": "boolean",
                                                        "description": "`true` — токены по этому сервису не ограничены. Ограничение на одновременные задачи это НЕ снимает."
                                                    },
                                                    "is_exceeded": {
                                                        "type": "boolean",
                                                        "description": "Лимит исчерпан: следующий заказ по этому сервису будет отклонён, пока счётчик не сбросится или не освободится слот."
                                                    },
                                                    "usage_percentage": {
                                                        "type": "number",
                                                        "description": "Доля израсходованного, 0–100. При безлимите всегда 0."
                                                    }
                                                },
                                                "required": [
                                                    "limit_id",
                                                    "service_id",
                                                    "service_code",
                                                    "service_name",
                                                    "entitlement_id",
                                                    "entitlement_code",
                                                    "limit_type",
                                                    "limit_value",
                                                    "used",
                                                    "remaining",
                                                    "period_start",
                                                    "period_end",
                                                    "resets_in",
                                                    "token_unlimited",
                                                    "is_exceeded",
                                                    "usage_percentage"
                                                ]
                                            },
                                            "description": "По одной записи на каждый лимит, доступный вашей подписке. Пусто — активной подписки нет, и генерация недоступна."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": [
                                            {
                                                "limit_id": 10,
                                                "service_id": 1,
                                                "service_code": "sfx",
                                                "service_name": "Sound Effects",
                                                "entitlement_id": null,
                                                "entitlement_code": null,
                                                "limit_type": "hourly",
                                                "limit_value": 100,
                                                "used": 0,
                                                "remaining": 100,
                                                "period_start": null,
                                                "period_end": null,
                                                "resets_in": null,
                                                "token_unlimited": false
                                            },
                                            {
                                                "limit_id": 11,
                                                "service_id": 2,
                                                "service_code": "music",
                                                "service_name": "Music Generation",
                                                "entitlement_id": null,
                                                "entitlement_code": null,
                                                "limit_type": "hourly",
                                                "limit_value": 100,
                                                "used": 0,
                                                "remaining": 100,
                                                "period_start": null,
                                                "period_end": null,
                                                "resets_in": null,
                                                "token_unlimited": false
                                            },
                                            {
                                                "limit_id": 12,
                                                "service_id": 3,
                                                "service_code": "elevenlabs",
                                                "service_name": "ElevenLabs TTS",
                                                "entitlement_id": 2,
                                                "entitlement_code": "elevenlabs_tts",
                                                "limit_type": "monthly",
                                                "limit_value": 500000,
                                                "used": 128400,
                                                "remaining": 371600,
                                                "period_start": "2026-07-01T00:00:00+00:00",
                                                "period_end": "2026-08-01T00:00:00+00:00",
                                                "resets_in": "2 weeks from now",
                                                "token_unlimited": false
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий API-ключ",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права billing.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/usage/{service}": {
            "get": {
                "operationId": "billing.usageService",
                "description": "То же, что `GET /usage`, но по одному сервису: расход, остаток и дата сброса счётчика.\n\nУдобно проверить перед крупной задачей, хватит ли квоты. Неизвестный или недоступный\nвам код сервиса → `404`.\n\n**Требуемое право ключа:** `billing.read`.",
                "summary": "Get Usage for Service",
                "tags": [
                    "Public Billing"
                ],
                "parameters": [
                    {
                        "name": "service",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Код сервиса — короткая латинская строка, а не число. Сейчас существуют:\n\n| Код | Что это |\n| --- | --- |\n| `elevenlabs` | Озвучка текста (TTS) на движке ElevenLabs |\n| `heygen` | Озвучка текста (TTS) на движке HeyGen |\n| `cartesia` | Озвучка текста (TTS) на движке Cartesia |\n| `fishaudio` | Озвучка текста (TTS) на движке Fish Audio |\n| `lumimg` | Генерация изображений |\n| `sfx` | Звуковые эффекты |\n| `music` | Музыкальные треки |\n| `voice_search` | Подбор похожего голоса по загруженному аудиофрагменту |\n\n**Где взять для своего аккаунта:** `GET /usage` возвращает `service_code` по\nкаждому доступному вам сервису — это ровно те значения, которые примет этот путь.\nТот же код лежит в поле `service_key` шаблона.\n\nСписок открыт: при подключении нового сервиса в нём появляются новые коды, поэтому\nзашивать таблицу выше в клиент не стоит. Незнакомый либо недоступный вам код → `404`.",
                        "example": "elevenlabs"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Лимиты для сервиса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "limit_id": {
                                                        "type": "integer",
                                                        "description": "Идентификатор записи лимита."
                                                    },
                                                    "service_id": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "ID сервиса, к которому относится лимит. `null` у лимитов, не привязанных к сервису."
                                                    },
                                                    "service_code": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Код сервиса — то самое значение для пути `GET /usage/{service}` и для `service_key` шаблона."
                                                    },
                                                    "service_name": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Название сервиса для показа, на языке аккаунта."
                                                    },
                                                    "entitlement_id": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "ID возможности подписки, которая выдала этот лимит."
                                                    },
                                                    "entitlement_code": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Код возможности подписки, выдавшей лимит."
                                                    },
                                                    "limit_type": {
                                                        "type": "string",
                                                        "description": "Что именно ограничивается. Две принципиально разные вещи:\n\n- **периодическая квота** — расход за период, обнуляется по `period_end`;\n- **слоты одновременных задач** — освобождаются сами, как только ваши\n  текущие заказы завершатся.\n\nИсчерпание первой ждёт конца периода, второй — минут."
                                                    },
                                                    "limit_value": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "Потолок по текущей подписке. `null` — потолка нет (см. `token_unlimited`)."
                                                    },
                                                    "used": {
                                                        "type": "integer",
                                                        "description": "Сколько израсходовано в текущем периоде."
                                                    },
                                                    "remaining": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ],
                                                        "description": "Сколько осталось. `null` при безлимите."
                                                    },
                                                    "period_start": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Начало текущего периода учёта, ISO 8601. `null`, если учёт в текущем периоде ещё не открыт (`used` при этом 0): лимит не трогали вовсе либо тратили только в прошлых периодах — период открывается первым расходом, а не по календарю."
                                                    },
                                                    "period_end": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Конец текущего периода учёта, ISO 8601 — в этот момент счётчик обнулится. `null` там же, где `period_start` (учёт в текущем периоде ещё не открыт), и у лимитов без периода (`concurrent`, `total`)."
                                                    },
                                                    "resets_in": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Сколько осталось до сброса счётчика, в человекочитаемом виде. `null` там же, где `period_end`."
                                                    },
                                                    "token_unlimited": {
                                                        "type": "boolean",
                                                        "description": "`true` — токены по этому сервису не ограничены. Ограничение на одновременные задачи это НЕ снимает."
                                                    },
                                                    "is_exceeded": {
                                                        "type": "boolean",
                                                        "description": "Лимит исчерпан: следующий заказ по этому сервису будет отклонён, пока счётчик не сбросится или не освободится слот."
                                                    },
                                                    "usage_percentage": {
                                                        "type": "number",
                                                        "description": "Доля израсходованного, 0–100. При безлимите всегда 0."
                                                    }
                                                },
                                                "required": [
                                                    "limit_id",
                                                    "service_id",
                                                    "service_code",
                                                    "service_name",
                                                    "entitlement_id",
                                                    "entitlement_code",
                                                    "limit_type",
                                                    "limit_value",
                                                    "used",
                                                    "remaining",
                                                    "period_start",
                                                    "period_end",
                                                    "resets_in",
                                                    "token_unlimited",
                                                    "is_exceeded",
                                                    "usage_percentage"
                                                ]
                                            },
                                            "description": "Лимиты по одному сервису — те же записи, что в `GET /usage`, но отфильтрованные. Обычно одна-две."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": [
                                            {
                                                "limit_id": 12,
                                                "service_id": 3,
                                                "service_code": "elevenlabs",
                                                "service_name": "ElevenLabs TTS",
                                                "entitlement_id": 2,
                                                "entitlement_code": "elevenlabs_tts",
                                                "limit_type": "monthly",
                                                "limit_value": 500000,
                                                "used": 128400,
                                                "remaining": 371600,
                                                "period_start": "2026-07-01T00:00:00+00:00",
                                                "period_end": "2026-08-01T00:00:00+00:00",
                                                "resets_in": "2 weeks from now",
                                                "token_unlimited": false
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Сервис не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий API-ключ",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права billing.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/cartesia": {
            "get": {
                "operationId": "cartesiaVoice.voices",
                "description": "Каталог голосов сервиса озвучки Cartesia: активные голоса с фильтрами по языку\n(ISO 639-1), полу и строке поиска. `voice_id` из ответа кладётся в\n`tts_settings.voice_id` шаблона с `service_key: cartesia`; параметр `voice_id`\nнаходит один голос по точному id — подпись голоса, уже выбранного в шаблоне.\n\nСвой клонированный голос в каталоге не появляется — его `voice_id` отдаёт\n`GET /voices/cartesia/clones` (только у клона в статусе `ready`). Голос,\nсклонированный вне Lumean (например, на cartesia.ai), тоже принимается: его id\nу Cartesia (UUID) кладётся в `tts_settings.voice_id` как есть.\n\nЕсли сервис закрыт для владельца ключа (закрытый тест), ответ — `404`\nс `reason: service_not_found`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "Cartesia Voices",
                "tags": [
                    "Public Cartesia Voices"
                ],
                "parameters": [
                    {
                        "name": "language",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 16
                        },
                        "description": "Язык голоса, код ISO 639-1 (`ru`, `en`, …).",
                        "example": "ru"
                    },
                    {
                        "name": "gender",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 32
                        },
                        "description": "Пол голоса в написании каталога Cartesia (`masculine`, `feminine`, `gender_neutral`).",
                        "example": "feminine"
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Поиск по названию и описанию голоса."
                    },
                    {
                        "name": "voice_id",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Точный `voice_id` голоса — найти подпись и превью голоса, уже выбранного в шаблоне, без перебора страниц. Снятый с каталога голос в ответ не попадает: пустой список.",
                        "example": "1e4176b1-3db9-44d6-a601-4fe68b041942"
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 200
                        },
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "example": 20
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "items": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/CartesiaVoice"
                                                    },
                                                    "description": "Голоса каталога Cartesia на этой странице."
                                                },
                                                "current_page": {
                                                    "type": "integer",
                                                    "description": "Номер текущей страницы, с единицы."
                                                },
                                                "last_page": {
                                                    "type": "integer",
                                                    "description": "Номер последней страницы."
                                                },
                                                "per_page": {
                                                    "type": "integer",
                                                    "description": "Размер страницы, применённый сервером."
                                                },
                                                "total": {
                                                    "type": "integer",
                                                    "description": "Сколько записей всего по фильтру."
                                                }
                                            },
                                            "required": [
                                                "items",
                                                "current_page",
                                                "last_page",
                                                "per_page",
                                                "total"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": 12,
                                                    "voice_id": "a0e99841-438c-4a64-b679-ae501e7d6091",
                                                    "name": "Russian Narrator",
                                                    "description": "Calm male narrator for audiobooks.",
                                                    "language": "ru",
                                                    "gender": "masculine",
                                                    "preview_url": "https://files.cartesia.ai/previews/a0e99841-438c-4a64-b679-ae501e7d6091.wav"
                                                },
                                                {
                                                    "id": 13,
                                                    "voice_id": "2ee87190-8f84-4925-97da-e52547f9462c",
                                                    "name": "Warm Storyteller",
                                                    "description": null,
                                                    "language": "ru",
                                                    "gender": "feminine",
                                                    "preview_url": null
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 50,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Сервис Cartesia закрыт для владельца ключа (`service_not_found`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Service not found.",
                                        "reason": "service_not_found",
                                        "error_code": "service_not_found"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/cartesia/clones/quota": {
            "get": {
                "operationId": "cartesiaVoice.quota",
                "description": "Квота клонов голоса Cartesia владельца ключа до попытки создания: `limit` — потолок\n(правило тарифа, иначе запасной потолок 2; `null`, если проверка отказала раньше\nподсчёта — бан клонирования или `no_subscription` у сервиса, закрытого для оплаты с\nбаланса), `used` — клоны в `pending`/`cloning`/`ready`, `remaining` — свободные места.\n\nЕсли создать клон сейчас нельзя, `can_create` равен `false`, а `reason` — тот же код,\nчто вернуло бы создание: `no_subscription`, `voice_clone_banned` (причина в\n`ban_reason`) или `voice_clone_quota_exceeded`.\n\nЕсли сервис закрыт для владельца ключа (закрытый тест), ответ — `404`\nс `reason: service_not_found`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "My Cartesia Voice Clone Quota",
                "tags": [
                    "Public Cartesia Voices"
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "limit": {
                                                    "type": [
                                                        "integer",
                                                        "null"
                                                    ],
                                                    "description": "Действующий потолок клонов: правило тарифа, иначе запасной потолок 2. `null`, если проверка отказала раньше подсчёта (бан клонирования или `no_subscription` у сервиса, закрытого для оплаты с баланса)."
                                                },
                                                "used": {
                                                    "type": "integer",
                                                    "description": "Сколько клонов держат место: в статусах `pending`, `cloning` и `ready`."
                                                },
                                                "remaining": {
                                                    "type": [
                                                        "integer",
                                                        "null"
                                                    ],
                                                    "description": "Свободные места: `limit - used`, не меньше 0; `null`, если `limit` равен `null`."
                                                },
                                                "can_create": {
                                                    "type": "boolean",
                                                    "description": "Можно ли создать клон прямо сейчас. `false` — причина в `reason`."
                                                },
                                                "reason": {
                                                    "type": [
                                                        "string",
                                                        "null"
                                                    ],
                                                    "description": "Код отказа, который вернуло бы создание: `no_subscription`, `voice_clone_banned` или `voice_clone_quota_exceeded`; `null`, если создать можно."
                                                },
                                                "ban_reason": {
                                                    "type": [
                                                        "string",
                                                        "null"
                                                    ],
                                                    "description": "Причина блокировки клонирования при `reason: voice_clone_banned`, иначе `null`."
                                                }
                                            },
                                            "required": [
                                                "limit",
                                                "used",
                                                "remaining",
                                                "can_create",
                                                "reason",
                                                "ban_reason"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "limit": 2,
                                            "used": 1,
                                            "remaining": 1,
                                            "can_create": true,
                                            "reason": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Сервис Cartesia закрыт для владельца ключа (`service_not_found`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/cartesia/clones": {
            "get": {
                "operationId": "cartesiaVoice.clones",
                "description": "Клоны голоса Cartesia владельца ключа, новые сверху. Архивные в список не входят —\nтолько с `status=archived`.\n\nУ клона в статусе `ready` есть `voice_id` — его кладут в `tts_settings.voice_id`\nшаблона Cartesia. Пока клон `pending`/`cloning`, `voice_id` равен `null`.\n\nСписок работает без проверки подписки: истёкшая подписка созданные клоны не прячет.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "My Cartesia Voice Clones",
                "tags": [
                    "Public Cartesia Voices"
                ],
                "parameters": [
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "$ref": "#/components/schemas/CartesiaVoiceCloneStatus"
                        },
                        "description": "Только клоны в этом статусе. Без параметра — все, кроме `archived`; архивные видны только с `status=archived`."
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Поиск по названию клона (`display_name`)."
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100
                        },
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "example": 20
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "items": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/CartesiaVoiceClone"
                                                    },
                                                    "description": "Ваши клоны голоса Cartesia на этой странице, новые сверху."
                                                },
                                                "current_page": {
                                                    "type": "integer",
                                                    "description": "Номер текущей страницы, с единицы."
                                                },
                                                "last_page": {
                                                    "type": "integer",
                                                    "description": "Номер последней страницы."
                                                },
                                                "per_page": {
                                                    "type": "integer",
                                                    "description": "Размер страницы, применённый сервером."
                                                },
                                                "total": {
                                                    "type": "integer",
                                                    "description": "Сколько записей всего по фильтру."
                                                }
                                            },
                                            "required": [
                                                "items",
                                                "current_page",
                                                "last_page",
                                                "per_page",
                                                "total"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": "01a10e08-2ee3-73c2-88b2-8df2bcef9d1d",
                                                    "provider": "cartesia",
                                                    "display_name": "Narrator (my voice)",
                                                    "language": "ru",
                                                    "description": null,
                                                    "status": "ready",
                                                    "voice_id": "6afd76cb-2663-46f7-8f74-85221ce58c56",
                                                    "attempts": 1,
                                                    "error": null,
                                                    "created_at": "2026-10-05T18:02:11+00:00",
                                                    "completed_at": "2026-10-05T18:03:40+00:00",
                                                    "failed_at": null,
                                                    "banned_at": null,
                                                    "ban_reason": null
                                                },
                                                {
                                                    "id": "01a10e08-2ee5-702c-b492-243c88fd966f",
                                                    "provider": "cartesia",
                                                    "display_name": "Noisy sample",
                                                    "language": "ru",
                                                    "description": null,
                                                    "status": "failed",
                                                    "voice_id": null,
                                                    "attempts": 1,
                                                    "error": {
                                                        "reason": "processing_error",
                                                        "message": "The provider rejected the sample."
                                                    },
                                                    "created_at": "2026-10-05T17:40:02+00:00",
                                                    "completed_at": null,
                                                    "failed_at": "2026-10-05T17:41:15+00:00",
                                                    "banned_at": null,
                                                    "ban_reason": null
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 20,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "cartesiaVoice.storeClone",
                "description": "Клонирует голос по образцу записи. Образец — base64 (можно data URI) в поле\n`reference_audio` JSON-тела, до 20 МБ. Согласие: `consent_biometric: true` (на обработку\nбиометрии — образца голоса) и `voice_owner` (`self` — ваш голос, `third_party` — чужой, есть\nсогласие владельца) пишутся в журнал согласий вместе с id клона; прежний\n`consent_confirmed: true` принимается вместо них, пока согласия не стали обязательными.\n\nКлонирование асинхронное. Ответ `201` приходит сразу с клоном в статусе `pending`\n(или сразу `failed` с `error.reason: dispatch_failed`, если очередь недоступна —\nтогда повторите через `POST /voices/cartesia/clones/{voiceClone}/retry`). Дальше\n`pending` → `cloning` → `ready` (или `failed`). Ход приходит WS-событием\n`voice_clone.updated` на канале `private-cartesia-voice-clones.{userId}`; без WS —\nопрашивайте `GET /voices/cartesia/clones`.\n\nПроверки по порядку: доступ к сервису (`404 service_not_found`), подписка (`402\nno_subscription`) — живая подписка с правом Cartesia нужна, только если сервис\nзакрыт для оплаты с баланса; иначе клонировать можно и без подписки, — потолок\nклонов (`422 voice_clone_quota_exceeded`, поле `max` — потолок): без подписки\nдоступно 2 клона (запасной потолок), с подпиской — лимит тарифа. Исчерпанная\nквота токенов клону не мешает.\n\n**Требуемое право ключа:** `voices.write`.",
                "summary": "Create Cartesia Voice Clone",
                "tags": [
                    "Public Cartesia Voices"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateCartesiaVoiceCloneRequest",
                                "example": {
                                    "display_name": "string",
                                    "reference_audio": "string",
                                    "language": "en",
                                    "description": "string",
                                    "consent_biometric": true,
                                    "voice_owner": "self",
                                    "consent_confirmed": "string"
                                }
                            },
                            "examples": {
                                "base64": {
                                    "summary": "Образец — чистый base64",
                                    "description": "reference_audio — содержимое файла в base64 (без переносов строк), до 20 МБ после декодирования; формат определяется по содержимому (WAV, MP3, M4A/MP4, AAC, OGG, WebM, FLAC). consent_biometric — строго true, voice_owner — self или third_party. Здесь в reference_audio пустой WAV для краткости: настоящий образец — 10–60 секунд чистой речи одного человека.",
                                    "value": {
                                        "display_name": "My voice",
                                        "reference_audio": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
                                        "language": "ru",
                                        "description": "Audiobook narrator",
                                        "consent_biometric": true,
                                        "voice_owner": "self"
                                    }
                                },
                                "data_uri": {
                                    "summary": "Образец — data URI, минимум полей",
                                    "description": "Префикс data:audio/...;base64, допускается и отбрасывается сервером. language и description можно не передавать.",
                                    "value": {
                                        "display_name": "Podcast voice",
                                        "reference_audio": "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
                                        "consent_biometric": true,
                                        "voice_owner": "self"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Клон поставлен в очередь",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/CartesiaVoiceClone",
                                            "description": "Созданный клон — в статусе `pending` (или сразу `failed` с `error.reason: dispatch_failed`, если очередь недоступна)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": "01a10e08-27fe-716f-b428-6bc739602ca6",
                                            "provider": "cartesia",
                                            "display_name": "My voice",
                                            "language": "ru",
                                            "description": null,
                                            "status": "pending",
                                            "voice_id": null,
                                            "attempts": 1,
                                            "error": null,
                                            "created_at": "2026-10-06T09:15:00+00:00",
                                            "completed_at": null,
                                            "failed_at": null,
                                            "banned_at": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Под этим кодом два разных тела. Ошибка валидации: `{success: false, message, errors: {поле: [текст]}}` без `reason` (нет обязательного поля, нет согласия `consent_biometric`/`consent_confirmed`, `language` не из 42 кодов, образец не base64, больше 20 МБ или не WAV/MP3/M4A/AAC/OGG/WebM/FLAC). Потолок клонов тарифа: `{success: false, message, reason: \"voice_clone_quota_exceeded\", error_code, max}` — `max` это потолок; заархивируйте клон (`DELETE`) или перейдите на старший тариф. Различайте по наличию `reason`",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Validation error",
                                        "errors": {
                                            "consent_confirmed": [
                                                "The consent confirmed field must be accepted."
                                            ],
                                            "reference_audio": [
                                                "The file is too large: 20 MB at most."
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышен лимит частоты клонирования (`rate_limit_exceeded`): по умолчанию 6 попыток в минуту и 60 в сутки на пользователя, общий с веб-кабинетом и LumVoice; считается каждая попытка, в том числе отклонённая валидацией. Ждать `retry_after` секунд (заголовок `Retry-After`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Нет живой подписки с правом Cartesia (`no_subscription`) — только если сервис закрыт для оплаты с баланса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "An active subscription is required.",
                                        "reason": "no_subscription",
                                        "error_code": "no_subscription"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Сервис Cartesia закрыт для владельца ключа (`service_not_found`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Service not found.",
                                        "reason": "service_not_found",
                                        "error_code": "service_not_found"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/cartesia/clones/{voiceClone}/retry": {
            "post": {
                "operationId": "cartesiaVoice.retryClone",
                "description": "Новая попытка клона в статусе `failed` с тем же образцом. Клон возвращается в\n`pending` и снова занимает место в потолке тарифа, поэтому проверки те же, что у\nсоздания (`404 service_not_found`, `402 no_subscription`,\n`422 voice_clone_quota_exceeded`).\n\nПовторить можно только `failed`: у `ready` голос уже есть, а `pending`/`cloning`\nещё идут — для них `409 voice_clone_not_retryable`.\n\n**Требуемое право ключа:** `voices.write`.",
                "summary": "Retry Cartesia Voice Clone",
                "tags": [
                    "Public Cartesia Voices"
                ],
                "parameters": [
                    {
                        "name": "voiceClone",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID клона голоса Cartesia или Fish Audio — 36 символов, не число.\n\nБерётся из поля `id` клона: его возвращает `POST /voices/cartesia/clones` (или\n`/voices/fishaudio/clones`) при создании и соответствующий `GET …/clones` в списке. Это НЕ `voice_id` клона (тот\nкладут в шаблон). Чужой клон → `404` с `reason: voice_clone_not_found`.",
                        "example": "0199b3a4-7c21-7d4e-9a8b-5f6e7d8c9b0a"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/CartesiaVoiceClone",
                                            "description": "Клон после постановки новой попытки — снова в `pending`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": "01a10e08-2ee5-702c-b492-243c88fd966f",
                                            "provider": "cartesia",
                                            "display_name": "Noisy sample",
                                            "language": "ru",
                                            "description": null,
                                            "status": "pending",
                                            "voice_id": null,
                                            "attempts": 2,
                                            "error": null,
                                            "created_at": "2026-10-05T17:40:02+00:00",
                                            "completed_at": null,
                                            "failed_at": null,
                                            "banned_at": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышен лимит частоты клонирования (`rate_limit_exceeded`): по умолчанию 6 попыток в минуту и 60 в сутки на пользователя, общий с веб-кабинетом и LumVoice; считается каждая попытка, в том числе отклонённая валидацией. Ждать `retry_after` секунд (заголовок `Retry-After`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Нет живой подписки с правом Cartesia (`no_subscription`) — только если сервис закрыт для оплаты с баланса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "An active subscription is required.",
                                        "reason": "no_subscription",
                                        "error_code": "no_subscription"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Клон не найден или чужой (`reason: voice_clone_not_found`). `voiceClone` не в формате UUID — маршрут не совпал, `reason: endpoint_not_found` (подсказка с адресом документации, llm.md §6.1). Также `reason: service_not_found` — сервис закрыт для владельца ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Voice clone not found.",
                                        "reason": "voice_clone_not_found",
                                        "error_code": "voice_clone_not_found"
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Клон не в `failed` (уже `ready`, ещё `pending`/`cloning` или `archived`) либо образец уже удалён (`reason: voice_clone_not_retryable`). Создайте новый клон",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Only a clone that ended with an error can be retried.",
                                        "reason": "voice_clone_not_retryable",
                                        "error_code": "voice_clone_not_retryable"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Потолок клонов тарифа (`voice_clone_quota_exceeded`, поле `max`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        },
                                        "max": {
                                            "type": "integer",
                                            "description": "Потолок клонов вашего тарифа: столько клонов в статусах `pending`, `cloning` и `ready` можно держать одновременно. Освободить место — архивировать ненужный клон."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code",
                                        "max"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Voice clone limit reached: no more than 3. Delete a clone you no longer need or wait for the current one to fail.",
                                        "reason": "voice_clone_quota_exceeded",
                                        "error_code": "voice_clone_quota_exceeded",
                                        "max": 3
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/cartesia/clones/{voiceClone}": {
            "delete": {
                "operationId": "cartesiaVoice.destroyClone",
                "description": "Снимает клон: он переходит в `archived`, в новые заказы больше не берётся, образец\nголоса удаляется, место в потолке тарифа освобождается. Заказы, уже принятые с этим\nголосом, не затрагиваются. Идемпотентно: повторный вызов отдаёт тот же архивный клон.\n\nРаботает без проверки подписки.\n\n**Требуемое право ключа:** `voices.write`.",
                "summary": "Archive Cartesia Voice Clone",
                "tags": [
                    "Public Cartesia Voices"
                ],
                "parameters": [
                    {
                        "name": "voiceClone",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID клона голоса Cartesia или Fish Audio — 36 символов, не число.\n\nБерётся из поля `id` клона: его возвращает `POST /voices/cartesia/clones` (или\n`/voices/fishaudio/clones`) при создании и соответствующий `GET …/clones` в списке. Это НЕ `voice_id` клона (тот\nкладут в шаблон). Чужой клон → `404` с `reason: voice_clone_not_found`.",
                        "example": "0199b3a4-7c21-7d4e-9a8b-5f6e7d8c9b0a"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/CartesiaVoiceClone",
                                            "description": "Клон в статусе `archived`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": "01a10e08-2ee3-73c2-88b2-8df2bcef9d1d",
                                            "provider": "cartesia",
                                            "display_name": "Narrator (my voice)",
                                            "language": "ru",
                                            "description": null,
                                            "status": "archived",
                                            "voice_id": "6afd76cb-2663-46f7-8f74-85221ce58c56",
                                            "attempts": 1,
                                            "error": null,
                                            "created_at": "2026-10-05T18:02:11+00:00",
                                            "completed_at": "2026-10-05T18:03:40+00:00",
                                            "failed_at": null,
                                            "banned_at": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Клон не найден или чужой (`reason: voice_clone_not_found`). `voiceClone` не в формате UUID — маршрут не совпал, `reason: endpoint_not_found` (подсказка с адресом документации, llm.md §6.1)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Voice clone not found.",
                                        "reason": "voice_clone_not_found",
                                        "error_code": "voice_clone_not_found"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/elevenlabs/library": {
            "get": {
                "operationId": "externalVoice.elevenlabsLibrary",
                "description": "Поиск по публичной библиотеке ElevenLabs (Voice Library) с расширенными\nфильтрами. `voice_id` из ответа кладётся в `tts_settings.voice_id` шаблона.\n\n⚠️ Результаты зависят от доступности внешнего API ElevenLabs и общей\nkey-инфраструктуры провайдера; при недоступности возвращается 503.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "ElevenLabs Voice Library",
                "tags": [
                    "Public External Voices"
                ],
                "parameters": [
                    {
                        "name": "search",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Поиск по названию голоса в библиотеке ElevenLabs."
                    },
                    {
                        "name": "sort",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Метод сортировки библиотеки ElevenLabs (проксируется провайдеру как есть).",
                        "example": "trending"
                    },
                    {
                        "name": "required_languages",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Оставить только голоса, поддерживающие указанные языки. Коды ISO-639-1 через запятую.",
                        "example": "ru,en"
                    },
                    {
                        "name": "accent",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Акцент голоса (`american`, `british`, …)."
                    },
                    {
                        "name": "gender",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255,
                            "enum": [
                                "male",
                                "female"
                            ]
                        },
                        "description": "Пол голоса."
                    },
                    {
                        "name": "age",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255,
                            "enum": [
                                "young",
                                "middle_aged",
                                "old"
                            ]
                        },
                        "description": "Возрастная категория голоса."
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": [
                                "integer",
                                "null"
                            ],
                            "minimum": 0
                        },
                        "description": "Номер страницы. Внимание: у каталогов внешних провайдеров нумерация **с нуля**.",
                        "example": 0
                    },
                    {
                        "name": "page_size",
                        "in": "query",
                        "schema": {
                            "type": [
                                "integer",
                                "null"
                            ],
                            "minimum": 1,
                            "maximum": 50
                        },
                        "description": "Размер страницы. По умолчанию 30, максимум 50.",
                        "example": 30
                    },
                    {
                        "name": "use_cases",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Сценарии использования (`narrative_story`, `social_media`, …), через запятую."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Список публичных голосов ElevenLabs",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "voices": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "additionalProperties": {}
                                                    },
                                                    "description": "Голоса библиотеки. Объект приходит от провайдера как есть, поэтому набор ключей задаёт он и он же может его изменить. Обязательный для работы — `voice_id`: его кладут в `config.voice_id` шаблона elevenlabs."
                                                },
                                                "total": {
                                                    "type": "integer",
                                                    "description": "Сколько голосов вернулось на этой странице."
                                                },
                                                "page": {
                                                    "type": "integer",
                                                    "description": "Номер страницы, с нуля — как в запросе."
                                                },
                                                "page_size": {
                                                    "type": "integer",
                                                    "description": "Размер страницы, применённый сервером."
                                                },
                                                "has_more": {
                                                    "type": "boolean",
                                                    "description": "Есть ли следующая страница. Ориентируйтесь на этот флаг, а не на `total`: тот считает только текущую выдачу."
                                                }
                                            },
                                            "required": [
                                                "voices",
                                                "total",
                                                "page",
                                                "page_size",
                                                "has_more"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "voices": [
                                                {
                                                    "public_owner_id": "4c9a8e2f6b1d4a3c9f7e0b2d5a8c1f3e6b9d0a2c4e7f1b3d5a8c0e2f4b6d9a1c",
                                                    "voice_id": "EXAVITQu4vr4xnSDxMaL",
                                                    "name": "Bella Storyteller",
                                                    "accent": "american",
                                                    "gender": "female",
                                                    "age": "young",
                                                    "descriptive": "calm",
                                                    "use_case": "narration",
                                                    "category": "professional",
                                                    "language": "en",
                                                    "description": "Warm, calm narrator voice ideal for long-form audiobooks and meditation content.",
                                                    "preview_url": "https://api.lumean.app/api/voice-preview?u=eyJpdiI6IjRjOWE4ZTJmNmIxZDRhM2MiLCJ2YWx1ZSI6ImZha2UifQ%3D%3D&signature=3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d",
                                                    "usage_character_count_1y": 18234567,
                                                    "usage_character_count_7d": 154302,
                                                    "cloned_by_count": 3120,
                                                    "free_users_allowed": true,
                                                    "notice_period": 0,
                                                    "instagram_username": null,
                                                    "twitter_username": null,
                                                    "youtube_username": null,
                                                    "tiktok_username": null,
                                                    "image_url": null,
                                                    "is_added_by_user": false,
                                                    "date_unix": 1748851200
                                                },
                                                {
                                                    "public_owner_id": "9a1c3e5f7b0d2a4c6e8f1b3d5a7c9e0f2b4d6a8c0e2f4b6d8a0c2e4f6b8d0a2c",
                                                    "voice_id": "yoZ06aMxZJJ28mfd3POQ",
                                                    "name": "Sam Ad Voice",
                                                    "accent": "british",
                                                    "gender": "male",
                                                    "age": "middle_aged",
                                                    "descriptive": "energetic",
                                                    "use_case": "advertisement",
                                                    "category": "professional",
                                                    "language": "en",
                                                    "description": "Punchy, upbeat voice for commercials and promotional reads.",
                                                    "preview_url": "https://api.lumean.app/api/voice-preview?u=eyJpdiI6IjlhMWMzZTVmN2IwZDJhNGMiLCJ2YWx1ZSI6ImZha2UifQ%3D%3D&signature=5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f",
                                                    "usage_character_count_1y": 4021553,
                                                    "usage_character_count_7d": 38210,
                                                    "cloned_by_count": 512,
                                                    "free_users_allowed": false,
                                                    "notice_period": 0,
                                                    "instagram_username": null,
                                                    "twitter_username": null,
                                                    "youtube_username": null,
                                                    "tiktok_username": null,
                                                    "image_url": null,
                                                    "is_added_by_user": false,
                                                    "date_unix": 1751328000
                                                }
                                            ],
                                            "total": 2,
                                            "page": 0,
                                            "page_size": 30,
                                            "has_more": false
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Внешний ElevenLabs API недоступен",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The voice provider is temporarily unavailable. Please try again later."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/heygen": {
            "get": {
                "operationId": "externalVoice.heygen",
                "description": "Каталог голосов HeyGen с фильтрацией и пагинацией. `voice_id` кладётся в\n`tts_settings.voice_id` шаблона heygen. Доступные значения фильтров —\nсм. `GET /api/public/voices/heygen/filters`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "HeyGen Voices",
                "tags": [
                    "Public External Voices"
                ],
                "parameters": [
                    {
                        "name": "gender",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 32,
                            "enum": [
                                "male",
                                "female",
                                "unknown"
                            ]
                        },
                        "description": "Пол голоса."
                    },
                    {
                        "name": "language",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 64
                        },
                        "description": "Язык голоса в написании HeyGen (`English`, `Spanish`, …). Список — `GET /voices/heygen/filters`.",
                        "example": "English"
                    },
                    {
                        "name": "accent",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 16
                        },
                        "description": "Регион-акцент (`en-US`, `en-GB`, …). Список — `GET /voices/heygen/filters`.",
                        "example": "en-US"
                    },
                    {
                        "name": "voice_engine",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 512
                        },
                        "description": "Движок рендера; несколько значений через запятую. Список — `GET /voices/heygen/filters`."
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 128
                        },
                        "description": "Поиск по имени голоса и его внутреннему `voice_name`."
                    },
                    {
                        "name": "sort",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "enum": [
                                "default",
                                "name",
                                "newest"
                            ]
                        },
                        "description": "Порядок выдачи. По умолчанию `default`."
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": [
                                "integer",
                                "null"
                            ],
                            "minimum": 0
                        },
                        "description": "Номер страницы. Нумерация **с нуля**.",
                        "example": 0
                    },
                    {
                        "name": "page_size",
                        "in": "query",
                        "schema": {
                            "type": [
                                "integer",
                                "null"
                            ],
                            "minimum": 1,
                            "maximum": 200
                        },
                        "description": "Размер страницы. По умолчанию 55.",
                        "example": 55
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Список голосов HeyGen",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "voices": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "voice_id": {
                                                                "type": "string",
                                                                "description": "Идентификатор голоса — кладётся в `config.voice_id` шаблона heygen."
                                                            },
                                                            "voice_name": {
                                                                "type": "string",
                                                                "description": "Исходное имя голоса у провайдера."
                                                            },
                                                            "display_name": {
                                                                "type": "string",
                                                                "description": "Название голоса для показа."
                                                            },
                                                            "gender": {
                                                                "type": "string",
                                                                "description": "Пол голоса. Подходит как значение фильтра `gender`."
                                                            },
                                                            "language": {
                                                                "type": "string",
                                                                "description": "Язык в написании провайдера (`English`, `Spanish`). Именно это значение принимает фильтр `language` — угадывать не нужно, берите отсюда или из `/voices/heygen/filters`."
                                                            },
                                                            "locale": {
                                                                "type": "string",
                                                                "description": "Локаль голоса, если провайдер её указал."
                                                            },
                                                            "accent": {
                                                                "type": "string",
                                                                "description": "Акцент в написании провайдера (`en-US`). Значение для фильтра `accent`."
                                                            },
                                                            "flag_url": {
                                                                "type": "string",
                                                                "description": "Ссылка на флажок языка — для оформления списка."
                                                            },
                                                            "preview_url": {
                                                                "type": "string",
                                                                "description": "Демо-фрагмент голоса. Ссылка ведёт на наш сервер, а не к провайдеру."
                                                            },
                                                            "voice_engines": {
                                                                "type": "array",
                                                                "items": {
                                                                    "type": "string"
                                                                },
                                                                "description": "Движки рендера, доступные этому голосу. Значения годятся для фильтра `voice_engine`."
                                                            },
                                                            "labels": {
                                                                "type": "array",
                                                                "items": {
                                                                    "type": "string"
                                                                },
                                                                "description": "Метки голоса от провайдера: манера, сценарий, эмоция."
                                                            },
                                                            "support_realtime": {
                                                                "type": "boolean",
                                                                "description": "Поддерживает ли голос потоковый режим у провайдера."
                                                            },
                                                            "emotion_support": {
                                                                "type": "boolean",
                                                                "description": "Принимает ли голос эмоциональные указания."
                                                            },
                                                            "support_locale": {
                                                                "type": "boolean",
                                                                "description": "Можно ли задать голосу локаль отдельно от языка."
                                                            }
                                                        },
                                                        "required": [
                                                            "voice_id",
                                                            "voice_name",
                                                            "display_name",
                                                            "gender",
                                                            "language",
                                                            "locale",
                                                            "accent",
                                                            "flag_url",
                                                            "preview_url",
                                                            "voice_engines",
                                                            "labels",
                                                            "support_realtime",
                                                            "emotion_support",
                                                            "support_locale"
                                                        ]
                                                    },
                                                    "description": "Голоса каталога HeyGen."
                                                },
                                                "total": {
                                                    "type": "integer",
                                                    "description": "Сколько голосов подходит под фильтры — по всем страницам."
                                                },
                                                "page": {
                                                    "type": "integer",
                                                    "description": "Номер страницы, с нуля."
                                                },
                                                "page_size": {
                                                    "type": "integer",
                                                    "description": "Размер страницы, применённый сервером."
                                                },
                                                "has_more": {
                                                    "type": "boolean",
                                                    "description": "Есть ли следующая страница."
                                                }
                                            },
                                            "required": [
                                                "voices",
                                                "total",
                                                "page",
                                                "page_size",
                                                "has_more"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "HeyGen voices retrieved successfully.",
                                        "data": {
                                            "voices": [
                                                {
                                                    "voice_id": "7f3a9c1e2b8d4f56a0c3e7f91b2d5a68",
                                                    "voice_name": "Yb56nzJ2XkQ0Fh1PmT8L",
                                                    "display_name": "Cassidy Pro",
                                                    "gender": "female",
                                                    "language": "English",
                                                    "locale": null,
                                                    "accent": "en-US",
                                                    "flag_url": "https://resource2.heygen.ai/flags/us.png",
                                                    "preview_url": "https://api.lumean.app/api/voice-preview?u=eyJpdiI6IjZmM2E5YzFlMmI4ZDRmNTYiLCJ2YWx1ZSI6ImZha2UifQ%3D%3D&signature=9f8e7d6c5b4a3928170f6e5d4c3b2a1908f7e6d5c4b3a2918f7e6d5c4b3a2918",
                                                    "voice_engines": [
                                                        "elevenLabs"
                                                    ],
                                                    "labels": [
                                                        "Conversational",
                                                        "Warm"
                                                    ],
                                                    "support_realtime": true,
                                                    "emotion_support": false,
                                                    "support_locale": false
                                                },
                                                {
                                                    "voice_id": "c159d4a2f6b7483d95e0a1c8f4b2d637",
                                                    "voice_name": "Xt4Wq8LmZ2VbPc6RfShu",
                                                    "display_name": "Marcus Energetic",
                                                    "gender": "male",
                                                    "language": "English",
                                                    "locale": null,
                                                    "accent": "en-GB",
                                                    "flag_url": "https://resource2.heygen.ai/flags/gb.png",
                                                    "preview_url": "https://api.lumean.app/api/voice-preview?u=eyJpdiI6ImMxNTlkNGEyZjZiNzQ4M2QiLCJ2YWx1ZSI6ImZha2UifQ%3D%3D&signature=1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
                                                    "voice_engines": [
                                                        "elevenLabs",
                                                        "azure"
                                                    ],
                                                    "labels": [
                                                        "Energetic"
                                                    ],
                                                    "support_realtime": false,
                                                    "emotion_support": true,
                                                    "support_locale": true
                                                }
                                            ],
                                            "total": 2,
                                            "page": 0,
                                            "page_size": 55,
                                            "has_more": false
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/heygen/filters": {
            "get": {
                "operationId": "externalVoice.heygenFilters",
                "description": "Справочник значений для фильтров каталога HeyGen: движки рендера, языки, акценты и\nлейблы — каждое со счётчиком публично видимых голосов.\n\nВызовите эту ручку перед `GET /voices/heygen`, чтобы знать, что можно подставить в\n`language`, `accent` и `voice_engine`: написание там провайдерское (`English`, `en-US`),\nугадывать его не нужно.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "HeyGen Voice Filters",
                "tags": [
                    "Public External Voices"
                ],
                "responses": {
                    "200": {
                        "description": "Доступные фильтры каталога голосов HeyGen",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "voice_engines": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "value": {
                                                                "type": "string",
                                                                "description": "Значение, которое подставляется в фильтр."
                                                            },
                                                            "count": {
                                                                "type": "integer",
                                                                "description": "Сколько голосов останется при этом значении."
                                                            }
                                                        },
                                                        "required": [
                                                            "value",
                                                            "count"
                                                        ]
                                                    },
                                                    "description": "Допустимые значения фильтра `voice_engine` со счётчиком голосов."
                                                },
                                                "genders": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "value": {
                                                                "type": "string",
                                                                "description": "Значение, которое подставляется в фильтр."
                                                            },
                                                            "count": {
                                                                "type": "integer",
                                                                "description": "Сколько голосов останется при этом значении."
                                                            }
                                                        },
                                                        "required": [
                                                            "value",
                                                            "count"
                                                        ]
                                                    },
                                                    "description": "Допустимые значения фильтра `gender` со счётчиком."
                                                },
                                                "languages": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "value": {
                                                                "type": "string",
                                                                "description": "Значение, которое подставляется в фильтр."
                                                            },
                                                            "count": {
                                                                "type": "integer",
                                                                "description": "Сколько голосов останется при этом значении."
                                                            }
                                                        },
                                                        "required": [
                                                            "value",
                                                            "count"
                                                        ]
                                                    },
                                                    "description": "Допустимые значения фильтра `language` — в написании провайдера, со счётчиком."
                                                },
                                                "accents": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "value": {
                                                                "type": "string",
                                                                "description": "Значение, которое подставляется в фильтр."
                                                            },
                                                            "count": {
                                                                "type": "integer",
                                                                "description": "Сколько голосов останется при этом значении."
                                                            }
                                                        },
                                                        "required": [
                                                            "value",
                                                            "count"
                                                        ]
                                                    },
                                                    "description": "Допустимые значения фильтра `accent` со счётчиком."
                                                },
                                                "labels": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "value": {
                                                                "type": "string",
                                                                "description": "Значение метки."
                                                            },
                                                            "count": {
                                                                "type": "integer",
                                                                "description": "Сколько голосов несёт эту метку."
                                                            }
                                                        },
                                                        "required": [
                                                            "value",
                                                            "count"
                                                        ]
                                                    },
                                                    "description": "Метки голосов со счётчиком. Отдельного фильтра по ним нет — ищите через `search`."
                                                }
                                            },
                                            "required": [
                                                "voice_engines",
                                                "genders",
                                                "languages",
                                                "accents",
                                                "labels"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "HeyGen voice filters retrieved successfully.",
                                        "data": {
                                            "voice_engines": [
                                                {
                                                    "value": "elevenLabs",
                                                    "count": 1820
                                                },
                                                {
                                                    "value": "azure",
                                                    "count": 410
                                                },
                                                {
                                                    "value": "cartesia",
                                                    "count": 98
                                                }
                                            ],
                                            "genders": [
                                                {
                                                    "value": "female",
                                                    "count": 1240
                                                },
                                                {
                                                    "value": "male",
                                                    "count": 1080
                                                },
                                                {
                                                    "value": "unknown",
                                                    "count": 10
                                                }
                                            ],
                                            "languages": [
                                                {
                                                    "value": "English",
                                                    "count": 640
                                                },
                                                {
                                                    "value": "Spanish",
                                                    "count": 210
                                                },
                                                {
                                                    "value": "French",
                                                    "count": 150
                                                }
                                            ],
                                            "accents": [
                                                {
                                                    "value": "en-US",
                                                    "count": 420
                                                },
                                                {
                                                    "value": "en-GB",
                                                    "count": 180
                                                },
                                                {
                                                    "value": "es-ES",
                                                    "count": 110
                                                }
                                            ],
                                            "labels": [
                                                {
                                                    "value": "Conversational",
                                                    "count": 530
                                                },
                                                {
                                                    "value": "Warm",
                                                    "count": 340
                                                },
                                                {
                                                    "value": "Energetic",
                                                    "count": 275
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/fishaudio": {
            "get": {
                "operationId": "fishAudioVoice.voices",
                "description": "Каталог голосов сервиса озвучки Fish Audio: активные голоса с фильтрами по языку\n(`ru`, `en`, `es`), полу (`male`/`female`) и строке поиска. `voice_id` из ответа кладётся\nв `tts_settings.voice_id` шаблона с `service_key: fishaudio`; параметр `voice_id` находит\nодин голос по точному id — подпись голоса, уже выбранного в шаблоне.\n\nЕсли сервис закрыт для владельца ключа (закрытый тест), ответ — `404`\nс `reason: service_not_found`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "Fish Audio Voices",
                "tags": [
                    "Public Fish Audio Voices"
                ],
                "parameters": [
                    {
                        "name": "language",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 16
                        },
                        "description": "Язык каталога: `ru`, `en` или `es`.",
                        "example": "ru"
                    },
                    {
                        "name": "gender",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 32
                        },
                        "description": "Пол голоса: `male` или `female`.",
                        "example": "female"
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Поиск по названию и описанию голоса."
                    },
                    {
                        "name": "voice_id",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Точный `voice_id` голоса — найти подпись и превью голоса, уже выбранного в шаблоне, без перебора страниц. Снятый с каталога голос в ответ не попадает: пустой список."
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 200
                        },
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "example": 20
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "items": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/FishAudioVoice"
                                                    },
                                                    "description": "Голоса каталога Fish Audio на этой странице."
                                                },
                                                "current_page": {
                                                    "type": "integer",
                                                    "description": "Номер текущей страницы, с единицы."
                                                },
                                                "last_page": {
                                                    "type": "integer",
                                                    "description": "Номер последней страницы."
                                                },
                                                "per_page": {
                                                    "type": "integer",
                                                    "description": "Размер страницы, применённый сервером."
                                                },
                                                "total": {
                                                    "type": "integer",
                                                    "description": "Сколько записей всего по фильтру."
                                                }
                                            },
                                            "required": [
                                                "items",
                                                "current_page",
                                                "last_page",
                                                "per_page",
                                                "total"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": 31,
                                                    "voice_id": "8ef4a238714b45718ce04243307c57a7",
                                                    "name": "Russian Narrator",
                                                    "description": "Calm male narrator.",
                                                    "language": "ru",
                                                    "languages": [
                                                        "ru"
                                                    ],
                                                    "gender": "male",
                                                    "tags": [
                                                        "male",
                                                        "narration"
                                                    ],
                                                    "author_name": "Fish",
                                                    "task_count": 120450,
                                                    "like_count": 380,
                                                    "preview_url": "https://s3.example.com/fishaudio/voice-previews/8ef4a238714b45718ce04243307c57a7.mp3?X-Amz-Expires=3600"
                                                },
                                                {
                                                    "id": 32,
                                                    "voice_id": "5c3a9e1b7d2f4086a1b4e0c9d8f72a63",
                                                    "name": "Warm Storyteller",
                                                    "description": null,
                                                    "language": "en",
                                                    "languages": [
                                                        "en",
                                                        "es"
                                                    ],
                                                    "gender": "female",
                                                    "tags": [
                                                        "female"
                                                    ],
                                                    "author_name": null,
                                                    "task_count": 8120,
                                                    "like_count": 54,
                                                    "preview_url": null
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 50,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Сервис Fish Audio закрыт для владельца ключа (`service_not_found`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Service not found.",
                                        "reason": "service_not_found",
                                        "error_code": "service_not_found"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/fishaudio/clones/quota": {
            "get": {
                "operationId": "fishAudioVoice.quota",
                "description": "Квота клонов голоса Fish Audio владельца ключа до попытки создания: `limit` — потолок\n(правило тарифа, иначе запасной потолок 2; `null`, если проверка отказала раньше\nподсчёта — бан клонирования или `no_subscription` у сервиса, закрытого для оплаты с\nбаланса), `used` — клоны в `pending`/`cloning`/`ready`, `remaining` — свободные места.\n\nЕсли создать клон сейчас нельзя, `can_create` равен `false`, а `reason` — тот же код,\nчто вернуло бы создание: `no_subscription`, `voice_clone_banned` (причина в\n`ban_reason`) или `voice_clone_quota_exceeded`.\n\nЕсли сервис закрыт для владельца ключа (закрытый тест), ответ — `404`\nс `reason: service_not_found`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "My Fish Audio Voice Clone Quota",
                "tags": [
                    "Public Fish Audio Voices"
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "limit": {
                                                    "type": [
                                                        "integer",
                                                        "null"
                                                    ],
                                                    "description": "Действующий потолок клонов: правило тарифа, иначе запасной потолок 2. `null`, если проверка отказала раньше подсчёта (бан клонирования или `no_subscription` у сервиса, закрытого для оплаты с баланса)."
                                                },
                                                "used": {
                                                    "type": "integer",
                                                    "description": "Сколько клонов держат место: в статусах `pending`, `cloning` и `ready`."
                                                },
                                                "remaining": {
                                                    "type": [
                                                        "integer",
                                                        "null"
                                                    ],
                                                    "description": "Свободные места: `limit - used`, не меньше 0; `null`, если `limit` равен `null`."
                                                },
                                                "can_create": {
                                                    "type": "boolean",
                                                    "description": "Можно ли создать клон прямо сейчас. `false` — причина в `reason`."
                                                },
                                                "reason": {
                                                    "type": [
                                                        "string",
                                                        "null"
                                                    ],
                                                    "description": "Код отказа, который вернуло бы создание: `no_subscription`, `voice_clone_banned` или `voice_clone_quota_exceeded`; `null`, если создать можно."
                                                },
                                                "ban_reason": {
                                                    "type": [
                                                        "string",
                                                        "null"
                                                    ],
                                                    "description": "Причина блокировки клонирования при `reason: voice_clone_banned`, иначе `null`."
                                                }
                                            },
                                            "required": [
                                                "limit",
                                                "used",
                                                "remaining",
                                                "can_create",
                                                "reason",
                                                "ban_reason"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "limit": 2,
                                            "used": 1,
                                            "remaining": 1,
                                            "can_create": true,
                                            "reason": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Сервис Fish Audio закрыт для владельца ключа (`service_not_found`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/fishaudio/clones": {
            "get": {
                "operationId": "fishAudioVoice.clones",
                "description": "Клоны голоса Fish Audio владельца ключа, новые сверху. Архивные в список не входят —\nтолько с `status=archived`.\n\nУ клона в статусе `ready` есть `voice_id` — его кладут в `tts_settings.voice_id`\nшаблона Fish Audio. Пока клон `pending`/`cloning`, `voice_id` равен `null`.\n\nСписок работает без проверки подписки: истёкшая подписка созданные клоны не прячет.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "My Fish Audio Voice Clones",
                "tags": [
                    "Public Fish Audio Voices"
                ],
                "parameters": [
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "$ref": "#/components/schemas/FishAudioVoiceCloneStatus"
                        },
                        "description": "Только клоны в этом статусе. Без параметра — все, кроме `archived`; архивные видны только с `status=archived`."
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 255
                        },
                        "description": "Поиск по названию клона (`display_name`)."
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100
                        },
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "example": 20
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "items": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/FishAudioVoiceClone"
                                                    },
                                                    "description": "Ваши клоны голоса Fish Audio на этой странице, новые сверху."
                                                },
                                                "current_page": {
                                                    "type": "integer",
                                                    "description": "Номер текущей страницы, с единицы."
                                                },
                                                "last_page": {
                                                    "type": "integer",
                                                    "description": "Номер последней страницы."
                                                },
                                                "per_page": {
                                                    "type": "integer",
                                                    "description": "Размер страницы, применённый сервером."
                                                },
                                                "total": {
                                                    "type": "integer",
                                                    "description": "Сколько записей всего по фильтру."
                                                }
                                            },
                                            "required": [
                                                "items",
                                                "current_page",
                                                "last_page",
                                                "per_page",
                                                "total"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": "01a10e08-2ee3-73c2-88b2-8df2bcef9d1e",
                                                    "provider": "fishaudio",
                                                    "display_name": "Narrator (my voice)",
                                                    "language": "ru",
                                                    "description": null,
                                                    "status": "ready",
                                                    "voice_id": "b3f1c9a07e5d4a2c8d6e1f0a9b7c5d34",
                                                    "attempts": 1,
                                                    "error": null,
                                                    "created_at": "2026-10-14T08:02:11+00:00",
                                                    "completed_at": "2026-10-14T08:03:40+00:00",
                                                    "failed_at": null,
                                                    "banned_at": null,
                                                    "ban_reason": null
                                                },
                                                {
                                                    "id": "01a10e08-2ee5-702c-b492-243c88fd9670",
                                                    "provider": "fishaudio",
                                                    "display_name": "Noisy sample",
                                                    "language": "ru",
                                                    "description": null,
                                                    "status": "failed",
                                                    "voice_id": null,
                                                    "attempts": 1,
                                                    "error": {
                                                        "reason": "processing_error",
                                                        "message": "The provider rejected the sample."
                                                    },
                                                    "created_at": "2026-10-14T07:40:02+00:00",
                                                    "completed_at": null,
                                                    "failed_at": "2026-10-14T07:41:15+00:00",
                                                    "banned_at": null,
                                                    "ban_reason": null
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 20,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "fishAudioVoice.storeClone",
                "description": "Клонирует голос по образцу записи. Образец — base64 (можно data URI) в поле\n`reference_audio` JSON-тела, до 20 МБ. Согласие: `consent_biometric: true` (на обработку\nбиометрии — образца голоса) и `voice_owner` (`self` — ваш голос, `third_party` — чужой, есть\nсогласие владельца) пишутся в журнал согласий вместе с id клона; прежний\n`consent_confirmed: true` принимается вместо них, пока согласия не стали обязательными.\n\nКлонирование асинхронное. Ответ `201` приходит сразу с клоном в статусе `pending`\n(или сразу `failed` с `error.reason: dispatch_failed`, если очередь недоступна —\nтогда повторите через `POST /voices/fishaudio/clones/{voiceClone}/retry`). Дальше\n`pending` → `cloning` → `ready` (или `failed`). Ход приходит WS-событием\n`voice_clone.updated` на канале `private-fishaudio-voice-clones.{userId}`; без WS —\nопрашивайте `GET /voices/fishaudio/clones`.\n\nПроверки по порядку: доступ к сервису (`404 service_not_found`), подписка (`402\nno_subscription`) — живая подписка с правом Fish Audio нужна, только если сервис\nзакрыт для оплаты с баланса; иначе клонировать можно и без подписки, — потолок\nклонов (`422 voice_clone_quota_exceeded`, поле `max` — потолок): без подписки\nдоступно 2 клона (запасной потолок), с подпиской — лимит тарифа. Те же правила,\nчто у Cartesia. Исчерпанная квота токенов клону не мешает.\n\n**Требуемое право ключа:** `voices.write`.",
                "summary": "Create Fish Audio Voice Clone",
                "tags": [
                    "Public Fish Audio Voices"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateFishAudioVoiceCloneRequest",
                                "example": {
                                    "display_name": "string",
                                    "reference_audio": "string",
                                    "language": "string",
                                    "description": "string",
                                    "consent_biometric": true,
                                    "voice_owner": "self",
                                    "consent_confirmed": "string"
                                }
                            },
                            "examples": {
                                "base64": {
                                    "summary": "Образец — чистый base64",
                                    "description": "reference_audio — содержимое файла в base64 (без переносов строк), до 20 МБ после декодирования; формат определяется по содержимому (WAV, MP3, M4A/MP4, AAC, OGG, WebM, FLAC). consent_biometric — строго true, voice_owner — self или third_party. Здесь в reference_audio пустой WAV для краткости: настоящий образец — 10–60 секунд чистой речи одного человека.",
                                    "value": {
                                        "display_name": "My voice",
                                        "reference_audio": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
                                        "language": "ru",
                                        "description": "Audiobook narrator",
                                        "consent_biometric": true,
                                        "voice_owner": "self"
                                    }
                                },
                                "data_uri": {
                                    "summary": "Образец — data URI, минимум полей",
                                    "description": "Префикс data:audio/...;base64, допускается и отбрасывается сервером. language и description можно не передавать.",
                                    "value": {
                                        "display_name": "Podcast voice",
                                        "reference_audio": "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
                                        "consent_biometric": true,
                                        "voice_owner": "self"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Клон поставлен в очередь",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/FishAudioVoiceClone",
                                            "description": "Созданный клон — в статусе `pending` (или сразу `failed` с `error.reason: dispatch_failed`, если очередь недоступна)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": "01a10e08-27fe-716f-b428-6bc739602ca7",
                                            "provider": "fishaudio",
                                            "display_name": "My voice",
                                            "language": "ru",
                                            "description": null,
                                            "status": "pending",
                                            "voice_id": null,
                                            "attempts": 1,
                                            "error": null,
                                            "created_at": "2026-10-14T09:15:00+00:00",
                                            "completed_at": null,
                                            "failed_at": null,
                                            "banned_at": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Под этим кодом два разных тела. Ошибка валидации: `{success: false, message, errors: {поле: [текст]}}` без `reason` (нет обязательного поля, нет согласия `consent_biometric`/`consent_confirmed`, `language` не двухбуквенный код ISO 639-1, образец не base64, больше 20 МБ или не WAV/MP3/M4A/AAC/OGG/WebM/FLAC). Потолок клонов тарифа: `{success: false, message, reason: \"voice_clone_quota_exceeded\", error_code, max}` — `max` это потолок; заархивируйте клон (`DELETE`) или перейдите на старший тариф. Различайте по наличию `reason`",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Validation error",
                                        "errors": {
                                            "consent_confirmed": [
                                                "The consent confirmed field must be accepted."
                                            ],
                                            "reference_audio": [
                                                "The file is too large: 20 MB at most."
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышен лимит частоты клонирования (`rate_limit_exceeded`): по умолчанию 6 попыток в минуту и 60 в сутки на пользователя, общий с веб-кабинетом и LumVoice; считается каждая попытка, в том числе отклонённая валидацией. Ждать `retry_after` секунд (заголовок `Retry-After`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Нет живой подписки с правом Fish Audio (`no_subscription`) — только если сервис закрыт для оплаты с баланса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "An active subscription is required.",
                                        "reason": "no_subscription",
                                        "error_code": "no_subscription"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Сервис Fish Audio закрыт для владельца ключа (`service_not_found`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Service not found.",
                                        "reason": "service_not_found",
                                        "error_code": "service_not_found"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/fishaudio/clones/{voiceClone}/retry": {
            "post": {
                "operationId": "fishAudioVoice.retryClone",
                "description": "Новая попытка клона в статусе `failed` с тем же образцом. Клон возвращается в\n`pending` и снова занимает место в потолке тарифа, поэтому проверки те же, что у\nсоздания (`404 service_not_found`, `402 no_subscription`,\n`422 voice_clone_quota_exceeded`).\n\nПовторить можно только `failed`: у `ready` голос уже есть, а `pending`/`cloning`\nещё идут — для них `409 voice_clone_not_retryable`.\n\n**Требуемое право ключа:** `voices.write`.",
                "summary": "Retry Fish Audio Voice Clone",
                "tags": [
                    "Public Fish Audio Voices"
                ],
                "parameters": [
                    {
                        "name": "voiceClone",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID клона голоса Cartesia или Fish Audio — 36 символов, не число.\n\nБерётся из поля `id` клона: его возвращает `POST /voices/cartesia/clones` (или\n`/voices/fishaudio/clones`) при создании и соответствующий `GET …/clones` в списке. Это НЕ `voice_id` клона (тот\nкладут в шаблон). Чужой клон → `404` с `reason: voice_clone_not_found`.",
                        "example": "0199b3a4-7c21-7d4e-9a8b-5f6e7d8c9b0a"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/FishAudioVoiceClone",
                                            "description": "Клон после постановки новой попытки — снова в `pending`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": "01a10e08-2ee5-702c-b492-243c88fd9670",
                                            "provider": "fishaudio",
                                            "display_name": "Noisy sample",
                                            "language": "ru",
                                            "description": null,
                                            "status": "pending",
                                            "voice_id": null,
                                            "attempts": 2,
                                            "error": null,
                                            "created_at": "2026-10-14T07:40:02+00:00",
                                            "completed_at": null,
                                            "failed_at": null,
                                            "banned_at": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышен лимит частоты клонирования (`rate_limit_exceeded`): по умолчанию 6 попыток в минуту и 60 в сутки на пользователя, общий с веб-кабинетом и LumVoice; считается каждая попытка, в том числе отклонённая валидацией. Ждать `retry_after` секунд (заголовок `Retry-After`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Нет живой подписки с правом Fish Audio (`no_subscription`) — только если сервис закрыт для оплаты с баланса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "An active subscription is required.",
                                        "reason": "no_subscription",
                                        "error_code": "no_subscription"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Клон не найден или чужой (`reason: voice_clone_not_found`). `voiceClone` не в формате UUID — маршрут не совпал, `reason: endpoint_not_found` (подсказка с адресом документации, llm.md §6.1). Также `reason: service_not_found` — сервис закрыт для владельца ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Voice clone not found.",
                                        "reason": "voice_clone_not_found",
                                        "error_code": "voice_clone_not_found"
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Клон не в `failed` (уже `ready`, ещё `pending`/`cloning` или `archived`) либо образец уже удалён (`reason: voice_clone_not_retryable`). Создайте новый клон",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Only a clone that ended with an error can be retried.",
                                        "reason": "voice_clone_not_retryable",
                                        "error_code": "voice_clone_not_retryable"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Потолок клонов тарифа (`voice_clone_quota_exceeded`, поле `max`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        },
                                        "max": {
                                            "type": "integer",
                                            "description": "Потолок клонов вашего тарифа на Fish Audio: столько клонов в статусах `pending`, `cloning` и `ready` можно держать одновременно (клоны Cartesia сюда не входят). Освободить место — архивировать ненужный клон."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code",
                                        "max"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Voice clone limit reached: no more than 3. Delete a clone you no longer need or wait for the current one to fail.",
                                        "reason": "voice_clone_quota_exceeded",
                                        "error_code": "voice_clone_quota_exceeded",
                                        "max": 3
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/fishaudio/clones/{voiceClone}": {
            "delete": {
                "operationId": "fishAudioVoice.destroyClone",
                "description": "Снимает клон: он переходит в `archived`, в новые заказы больше не берётся, образец\nголоса удаляется, место в потолке тарифа освобождается. Заказы, уже принятые с этим\nголосом, не затрагиваются. Идемпотентно: повторный вызов отдаёт тот же архивный клон.\n\nРаботает без проверки подписки.\n\n**Требуемое право ключа:** `voices.write`.",
                "summary": "Archive Fish Audio Voice Clone",
                "tags": [
                    "Public Fish Audio Voices"
                ],
                "parameters": [
                    {
                        "name": "voiceClone",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID клона голоса Cartesia или Fish Audio — 36 символов, не число.\n\nБерётся из поля `id` клона: его возвращает `POST /voices/cartesia/clones` (или\n`/voices/fishaudio/clones`) при создании и соответствующий `GET …/clones` в списке. Это НЕ `voice_id` клона (тот\nкладут в шаблон). Чужой клон → `404` с `reason: voice_clone_not_found`.",
                        "example": "0199b3a4-7c21-7d4e-9a8b-5f6e7d8c9b0a"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/FishAudioVoiceClone",
                                            "description": "Клон в статусе `archived`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": "01a10e08-2ee3-73c2-88b2-8df2bcef9d1e",
                                            "provider": "fishaudio",
                                            "display_name": "Narrator (my voice)",
                                            "language": "ru",
                                            "description": null,
                                            "status": "archived",
                                            "voice_id": "b3f1c9a07e5d4a2c8d6e1f0a9b7c5d34",
                                            "attempts": 1,
                                            "error": null,
                                            "created_at": "2026-10-14T08:02:11+00:00",
                                            "completed_at": "2026-10-14T08:03:40+00:00",
                                            "failed_at": null,
                                            "banned_at": null,
                                            "ban_reason": null
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Клон не найден или чужой (`reason: voice_clone_not_found`). `voiceClone` не в формате UUID — маршрут не совпал, `reason: endpoint_not_found` (подсказка с адресом документации, llm.md §6.1)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Voice clone not found.",
                                        "reason": "voice_clone_not_found",
                                        "error_code": "voice_clone_not_found"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders": {
            "get": {
                "operationId": "order.index",
                "description": "Возвращает пагинированный список заказов пользователя (владельца ключа).\n\n**Фильтрация:**\n\n- `status` — статус заказа. Допустимые значения: `created`, `pending`,\n  `in_progress`, `completed`, `result_delivered`, `failed`, `compensated`,\n  `cancelled`, `partially_completed`. Неизвестное значение → 422.\n- `template_id` — UUID шаблона.\n- `task_type[]` — тип задачи, множественный: `?task_type[]=tts&task_type[]=sfx`.\n  Известные значения: `tts`, `image`, `txt2img`, `remix`, `image_edit`,\n  `voice_clone`, `sfx`, `music`, `voice_search`. Список расширяется при\n  подключении новых сервисов; неизвестный тип вернёт пустой список, а не ошибку.\n\n**Терминальные статусы** (заказ больше не изменится): `completed`,\n`result_delivered`, `failed`, `compensated`, `cancelled`.\n\n**Требуемое право ключа:** `orders.read`.",
                "summary": "List Orders",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100
                        },
                        "description": "Записей на странице, `1..100`. По умолчанию 20.",
                        "example": 20
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "description": "Вернуть только заказы в этом статусе. Неизвестное значение → `422`.\n\nТерминальные (заказ больше не изменится): `completed`, `result_delivered`, `failed`,\n`compensated`, `cancelled`. Нетерминальный `partially_completed` означает, что часть\nчанков не удалась и ждёт вашего действия — см. `POST /orders/{order}/items/{item}/retry`.",
                        "schema": {
                            "$ref": "#/components/schemas/OrderStatus"
                        },
                        "example": "completed"
                    },
                    {
                        "name": "template_id",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "format": "uuid"
                        },
                        "description": "Вернуть только заказы, созданные по этому шаблону (UUID из `GET /templates`).",
                        "example": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c"
                    },
                    {
                        "name": "task_type[]",
                        "in": "query",
                        "schema": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            }
                        },
                        "description": "Фильтр по типу задачи, **множественный**: `?task_type[]=tts&task_type[]=sfx`.\nОдиночная строка (`?task_type=tts`) тоже принимается.\n\nИзвестные значения: `tts`, `image`, `txt2img`, `remix`, `image_edit`, `voice_clone`,\n`sfx`, `music`, `voice_search`. Список расширяется при подключении новых сервисов,\nпоэтому фильтр намеренно мягкий: неизвестный тип вернёт пустой список, а не `422`.",
                        "example": "tts"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Список заказов",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/OrderList",
                                            "description": "Страница заказов владельца ключа, новые сверху. Чанков (`items`) в списке нет — за ними идите в `GET /orders/{order}`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Orders list retrieved successfully.",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": "c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
                                                    "user_id": 501,
                                                    "template": null,
                                                    "template_id": null,
                                                    "task_type": "music",
                                                    "status": "completed",
                                                    "price_units": 6000,
                                                    "total_tokens": 6000,
                                                    "task_data": {
                                                        "n_variants": 3,
                                                        "lyrics_text": null,
                                                        "force_instrumental": true,
                                                        "music_length_ms": 30000,
                                                        "model_id": "music_v2",
                                                        "prompt": "Lo-fi hip hop, calm, rainy night, vinyl crackle"
                                                    },
                                                    "result": {
                                                        "user_message": null,
                                                        "files": [
                                                            "storage/501/orders/c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f/output/chunks/0/result.mp3",
                                                            "storage/501/orders/c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f/output/chunks/1/result.mp3",
                                                            "storage/501/orders/c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f/output/chunks/2/result.mp3"
                                                        ]
                                                    },
                                                    "created_at": "2026-07-17T12:10:00+00:00",
                                                    "updated_at": "2026-07-17T12:11:20+00:00",
                                                    "eta_seconds": null,
                                                    "eta_at": null,
                                                    "completed_chunks": 3,
                                                    "total_chunks": 3,
                                                    "progress_percent": 100,
                                                    "items": [
                                                        {
                                                            "id": "d8e9f0a1-2b3c-4d4e-8f5a-6b7c8d9e0f1a",
                                                            "order_id": "c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
                                                            "item_type": "chunk",
                                                            "chunk_index": 0,
                                                            "parent_item_id": null,
                                                            "original_text_length": 0,
                                                            "current_text_length": 0,
                                                            "price_units": 2000,
                                                            "consumed_units": 2000,
                                                            "remaining_locked": 0,
                                                            "status": "completed",
                                                            "result_file": "storage/501/orders/c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f/output/chunks/0/result.mp3",
                                                            "attempt_number": 1,
                                                            "can_retry": false,
                                                            "can_regenerate": true,
                                                            "created_at": "2026-07-17T12:10:05+00:00",
                                                            "updated_at": "2026-07-17T12:11:00+00:00"
                                                        },
                                                        {
                                                            "id": "e9f0a1b2-3c4d-4e5f-8a6b-7c8d9e0f1a2b",
                                                            "order_id": "c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
                                                            "item_type": "chunk",
                                                            "chunk_index": 1,
                                                            "parent_item_id": null,
                                                            "original_text_length": 0,
                                                            "current_text_length": 0,
                                                            "price_units": 2000,
                                                            "consumed_units": 2000,
                                                            "remaining_locked": 0,
                                                            "status": "completed",
                                                            "result_file": "storage/501/orders/c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f/output/chunks/1/result.mp3",
                                                            "attempt_number": 1,
                                                            "can_retry": false,
                                                            "can_regenerate": true,
                                                            "created_at": "2026-07-17T12:10:05+00:00",
                                                            "updated_at": "2026-07-17T12:11:10+00:00"
                                                        },
                                                        {
                                                            "id": "f0a1b2c3-4d5e-4f6a-8b7c-8d9e0f1a2b3c",
                                                            "order_id": "c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
                                                            "item_type": "chunk",
                                                            "chunk_index": 2,
                                                            "parent_item_id": null,
                                                            "original_text_length": 0,
                                                            "current_text_length": 0,
                                                            "price_units": 2000,
                                                            "consumed_units": 2000,
                                                            "remaining_locked": 0,
                                                            "status": "completed",
                                                            "result_file": "storage/501/orders/c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f/output/chunks/2/result.mp3",
                                                            "attempt_number": 1,
                                                            "can_retry": false,
                                                            "can_regenerate": true,
                                                            "created_at": "2026-07-17T12:10:05+00:00",
                                                            "updated_at": "2026-07-17T12:11:20+00:00"
                                                        }
                                                    ]
                                                },
                                                {
                                                    "id": "b2c3d4e5-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
                                                    "user_id": 501,
                                                    "template": null,
                                                    "template_id": null,
                                                    "task_type": "sfx",
                                                    "status": "in_progress",
                                                    "price_units": 50,
                                                    "total_tokens": 0,
                                                    "task_data": {
                                                        "prompt_influence": 0.3,
                                                        "duration_seconds": 6,
                                                        "loop": false,
                                                        "output_format": "mp3_44100_128",
                                                        "text": "Footsteps on gravel, then a door creaks open."
                                                    },
                                                    "result": null,
                                                    "created_at": "2026-07-17T12:05:00+00:00",
                                                    "updated_at": "2026-07-17T12:05:00+00:00",
                                                    "eta_seconds": 45,
                                                    "eta_at": "2026-07-17T12:05:45+00:00",
                                                    "completed_chunks": 0,
                                                    "total_chunks": 1,
                                                    "progress_percent": 0,
                                                    "items": [
                                                        {
                                                            "id": "a6b7c8d9-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
                                                            "order_id": "b2c3d4e5-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
                                                            "item_type": "chunk",
                                                            "chunk_index": 0,
                                                            "parent_item_id": null,
                                                            "original_text_length": 0,
                                                            "current_text_length": 0,
                                                            "price_units": 50,
                                                            "consumed_units": 0,
                                                            "remaining_locked": 50,
                                                            "status": "processing",
                                                            "result_file": null,
                                                            "attempt_number": 1,
                                                            "can_retry": false,
                                                            "can_regenerate": false,
                                                            "created_at": "2026-07-17T12:05:00+00:00",
                                                            "updated_at": "2026-07-17T12:05:00+00:00"
                                                        }
                                                    ]
                                                },
                                                {
                                                    "id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                                    "user_id": 501,
                                                    "template": {
                                                        "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                                        "user_id": 501,
                                                        "slug": null,
                                                        "name": "Narrator Voice (EN)",
                                                        "service_key": "elevenlabs",
                                                        "service": {
                                                            "id": 3,
                                                            "code": "elevenlabs",
                                                            "name": "ElevenLabs",
                                                            "display_name": null,
                                                            "payg_access": "everyone",
                                                            "billing_mode": "pay_as_you_go",
                                                            "unit_asset_id": 5,
                                                            "unit_asset": {
                                                                "id": 5,
                                                                "code": "tts_units",
                                                                "kind": "unit",
                                                                "precision": 0,
                                                                "name": "TTS Units",
                                                                "deleted_at": null,
                                                                "created_at": "2026-01-10T09:00:00+00:00",
                                                                "updated_at": "2026-01-10T09:00:00+00:00"
                                                            },
                                                            "required_entitlement_id": null,
                                                            "units_per_call": null,
                                                            "units_per_char": 1,
                                                            "units_per_image": null,
                                                            "units_per_second": null,
                                                            "min_billable_chars": null,
                                                            "lumcoin_per_token": null,
                                                            "lumc_precision": 2,
                                                            "price_lumc_minor": null,
                                                            "unit_size": 1,
                                                            "uses_money_first_pricing": false,
                                                            "rate_unit": null,
                                                            "display_category": null,
                                                            "is_premium": false,
                                                            "display_order": 0,
                                                            "metadata": null,
                                                            "is_active": true,
                                                            "created_at": "2026-01-10T09:00:00+00:00",
                                                            "updated_at": "2026-01-10T09:00:00+00:00"
                                                        },
                                                        "config": null,
                                                        "is_public": false,
                                                        "folder_id": null,
                                                        "created_at": "2026-06-01T10:00:00+00:00",
                                                        "updated_at": "2026-06-01T10:00:00+00:00"
                                                    },
                                                    "template_id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                                    "task_type": "tts",
                                                    "status": "completed",
                                                    "price_units": 420,
                                                    "total_tokens": 420,
                                                    "task_data": {
                                                        "text": "Welcome to our platform! This narration greets every new listener.",
                                                        "total_length": 420
                                                    },
                                                    "result": {
                                                        "user_message": null,
                                                        "files": [
                                                            "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/result.mp3"
                                                        ],
                                                        "service_files": [
                                                            "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/service/subtitles.srt",
                                                            "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/service/subtitles.vtt",
                                                            "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/service/result.json"
                                                        ]
                                                    },
                                                    "created_at": "2026-07-17T12:00:00+00:00",
                                                    "updated_at": "2026-07-17T12:02:50+00:00",
                                                    "eta_seconds": null,
                                                    "eta_at": null,
                                                    "completed_chunks": 2,
                                                    "total_chunks": 2,
                                                    "progress_percent": 100,
                                                    "items": [
                                                        {
                                                            "id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
                                                            "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                                            "item_type": "chunk",
                                                            "chunk_index": 0,
                                                            "parent_item_id": null,
                                                            "original_text_length": 240,
                                                            "current_text_length": 240,
                                                            "price_units": 240,
                                                            "consumed_units": 240,
                                                            "remaining_locked": 0,
                                                            "status": "completed",
                                                            "result_file": {
                                                                "path": "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/chunks/0/result.mp3",
                                                                "storage": "s3",
                                                                "name": "chunk_0.mp3",
                                                                "size": 512340,
                                                                "mime_type": "audio/mpeg",
                                                                "size_formatted": "500.33 KB"
                                                            },
                                                            "attempt_number": 1,
                                                            "can_retry": false,
                                                            "can_regenerate": true,
                                                            "created_at": "2026-07-17T12:00:05+00:00",
                                                            "updated_at": "2026-07-17T12:02:10+00:00"
                                                        },
                                                        {
                                                            "id": "e5f6a7b8-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                                                            "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                                            "item_type": "chunk",
                                                            "chunk_index": 1,
                                                            "parent_item_id": null,
                                                            "original_text_length": 180,
                                                            "current_text_length": 180,
                                                            "price_units": 180,
                                                            "consumed_units": 180,
                                                            "remaining_locked": 0,
                                                            "status": "completed",
                                                            "result_file": {
                                                                "path": "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/chunks/1/result.mp3",
                                                                "storage": "s3",
                                                                "name": "chunk_1.mp3",
                                                                "size": 398210,
                                                                "mime_type": "audio/mpeg",
                                                                "size_formatted": "388.88 KB"
                                                            },
                                                            "attempt_number": 1,
                                                            "can_retry": false,
                                                            "can_regenerate": true,
                                                            "created_at": "2026-07-17T12:00:05+00:00",
                                                            "updated_at": "2026-07-17T12:02:45+00:00"
                                                        }
                                                    ]
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 20,
                                            "total": 3
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права orders.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "order.store",
                "description": "Создаёт заказ на генерацию от имени владельца ключа и сразу ставит его в очередь.\nОтвет приходит немедленно, со статусом `created`/`pending` — результата в нём ещё нет.\n\n## Два способа создать заказ\n\n**По шаблону** — `template_id` + `input_text`. Шаблон задаёт сервис, тип задачи, голос\nи все настройки; в заказе остаётся прислать только текст. Разово отклониться от настроек\nшаблона можно через `config_override`, не создавая новый шаблон.\n\n**Без шаблона** — `task_type` + `task_data`. Так работают `sfx` и `music` (весь промпт\nи параметры в `task_data`) и `voice_search` (аудиофрагмент в `input_files`). Для `tts`\nнужен шаблон — в нём выбор голоса и модели.\n\nОдно из двух полей (`template_id` или `task_type`) обязательно. Полный разбор каждого\nполя и готовые примеры — во вкладке **Body**.\n\n## Как режется текст\n\nПо умолчанию текст режется на фрагменты подряд, по техническому размеру: границы\nабзацев роли не играют. **Абзацный режим** делит текст сначала по пустой строке, и\nабзац становится самостоятельной единицей генерации — в ответе появляются\n`paragraphs[]`, а у фрагментов `paragraph_index`.\n\nВключается ключом `generation_mode` в `config_override` (пример — во вкладке **Body**,\nсценарий «TTS по абзацам»). Учтите: минимальный тарифицируемый объём в этом режиме\nсчитается **по каждому абзацу**. Подробности — в описании поля `config_override`.\n\n## Разметка внутри текста (озвучка)\n\nЧасть текста озвучке не подлежит: сервер вырезает её перед генерацией и **не берёт за\nнеё денег**. Распознаются ровно три вида разметки:\n\n| Вид | Пример | Что делает |\n| --- | --- | --- |\n| Пауза | `{{pause=2.5}}`, `{{pause:500ms}}`, `{{pause}}` | Тишина указанной длины; без значения — 1 секунда |\n| Пауза SSML | `<break time=\"2s\"/>`, `<break time=\"500ms\"/>` | То же самое в SSML-нотации |\n| Указание диктору | `[шёпот]`, `[Тихий голос]` | Подсказка модели о манере чтения |\n\n> **Квадратные скобки — это ВСЯ разметка, а не список ключевых слов.** Вырезается любой\n> фрагмент вида `[…]` без переноса строки внутри. Сноска `[1]`, пометка `[sic]`,\n> тайм-код `[00:12]`, ссылка `[рис. 3]` — всё это исчезнет из озвучки. Если квадратные\n> скобки должны прозвучать, замените их.\n\nОбратная сторона: **круглые и угловые скобки, звёздочки и прочая типографика\nозвучиваются и тарифицируются**. Ремарки сценария `(пауза)`, `<Fade out>`, `*шёпотом*`\nдиктор прочитает вслух — их нужно убирать самим либо переводить в `[…]`.\n\n### Ограничения размера и пауз\n\nПроверяются до создания заказа, превышение → `422`, деньги не резервируются:\n\n| Ограничение | Значение |\n| --- | --- |\n| Фрагментов в заказе | не больше 1500 |\n| Пауз в тексте | не больше 500 |\n| Одна пауза | не длиннее 10 секунд |\n| Суммарная тишина | не больше 3600 секунд |\n\nСчитаются только паузы; указания диктору в `[…]` под эти лимиты не подпадают.\n\n**Фрагменты — это не символы.** Сколько их выйдет, показывает\n`POST /orders/chunks/preview` (`summary.chunks_count`); там же превышение видно\nзаранее как `caps_ok=false` + `caps_exceeded=\"chunks\"`, без отказа. Предел\nтехнический: финал заказа собирается из фрагментов одной операцией, и сверх\nэтого числа она не успевает завершиться. Текст, который упирается в потолок,\nнужно разделить на несколько заказов.\n\nОтдельный отказ — текст, в котором **после вырезания разметки не осталось ничего\nпроизносимого**: только теги, только знаки препинания (`...`, `!!!`, `—`) или только\nскобочные ремарки. Такой заказ не создаётся.\n\n## Что сервер меняет в тексте\n\nПеред нарезкой текст приводится к канону — сохранённый заказ и все смещения фрагментов\nсчитаются уже от изменённого текста:\n\n| Что | Как |\n| --- | --- |\n| Переносы строк | `\\r\\n` и `\\r` → `\\n` |\n| Табуляция | Заменяется пробелом |\n| Больше 3 переносов подряд | Схлопываются до 3 |\n| Больше 5 пробелов подряд | Схлопываются до 5 |\n| `Привет.Мир` (только озвучка) | Пробел после `.`, `!`, `?` перед буквой или цифрой → `Привет. Мир` |\n\nПоследнее правило не трогает десятичные (`3.14`), сокращения (`т.д.`), акронимы\n(`U.S.A.`), URL со схемой, email, многоточия и содержимое тегов разметки. Голый домен\nбез схемы (`example.com` в середине фразы) от `Привет.Мир` неотличим и пробел получит —\nпишите такие адреса со схемой.\n\n## Что делать после ответа\n\n1. Запомните `id` заказа из ответа.\n2. Дождитесь готовности: подпишитесь на WebSocket-канал `private-order.task.{orderId}`\n   (без поллинга) либо опрашивайте `GET /orders/{order}`.\n3. Терминальные статусы: `completed`, `result_delivered`, `failed`, `compensated`,\n   `cancelled`. Отдельно `partially_completed` — часть чанков не удалась и ждёт вашего\n   решения (см. `POST /orders/{order}/items/{item}/retry`).\n4. Файлы результата — в `result.files[]`; ссылку на скачивание даёт `POST /storage/url`.\n\n## Оплата\n\nСначала расходуется квота подписки, и только остаток — из LMC-баланса: если квоты не\nхватило, добор списывается автоматически и заказ создаётся сразу (`201`). Не хватает\nденег на добор — `402` `insufficient_balance`. Поля `confirm_payg_topup` и `quote_token`\nнужны только в режиме отката к явному подтверждению (его включает оператор платформы:\nтогда вместо заказа приходит `402` `payg_topup_required` с суммой добора) — подробности\nв описании поля `confirm_payg_topup`.\n\nСтоимость заказа списывается с токен-квоты вашего ключа. Проверка идёт **до** создания\nзаказа: при превышении вернётся `429`, заказ не создастся и файлы не загрузятся.\n\nПровенанс (`source=api`, `api_key_id`) проставляет сервер — передавать его не нужно.\n\n**Требуемое право ключа:** `orders.write`.",
                "summary": "Create Order",
                "tags": [
                    "Public Orders"
                ],
                "requestBody": {
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "$ref": "#/components/schemas/StoreOrderRequest",
                                "example": {
                                    "template_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                    "task_type": "tts",
                                    "name": "string",
                                    "task_data": [],
                                    "config_override": [],
                                    "skip_service_defaults": true,
                                    "input_text": "string",
                                    "confirm_payg_topup": true,
                                    "quote_token": "string",
                                    "input_files": [
                                        "<binary>"
                                    ]
                                }
                            },
                            "examples": {
                                "tts_template": {
                                    "summary": "TTS по шаблону",
                                    "description": "Обычный путь озвучки: настройки голоса и модели лежат в шаблоне, в заказе — только текст.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Здравствуйте! Рады видеть вас в Lumean.",
                                        "name": "Приветствие для лендинга"
                                    }
                                },
                                "tts_paragraph": {
                                    "summary": "TTS по абзацам",
                                    "description": "Абзацный режим: текст делится по пустой строке, абзац становится единицей генерации. В ответе появятся paragraphs[]. Минимальный тарифицируемый объём считается по КАЖДОМУ абзацу — короткие абзацы дороже, чем тот же текст сплошняком.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Первый абзац — вступление, его озвучиваем отдельно.\n\nВторой абзац продолжает мысль и тоже идёт своей единицей.\n\nТретий абзац завершает фрагмент.",
                                        "name": "Глава 1 — по абзацам",
                                        "config_override": {
                                            "generation_mode": "paragraph"
                                        }
                                    }
                                },
                                "tts_override": {
                                    "summary": "TTS другим голосом",
                                    "description": "Тот же шаблон, но разово другим голосом — через config_override, без создания нового шаблона.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Здравствуйте! Рады видеть вас в Lumean.",
                                        "config_override": {
                                            "tts_settings": {
                                                "voice_id": "EXAVITQu4vr4xnSDxMaL",
                                                "advanced_voice_settings": true,
                                                "voice_settings": {
                                                    "stability": 0.5,
                                                    "similarity_boost": 0.75
                                                }
                                            }
                                        }
                                    }
                                },
                                "sfx": {
                                    "summary": "SFX без шаблона",
                                    "description": "Звуковой эффект: шаблон не нужен, все параметры в task_data. Одна генерация = один файл.",
                                    "value": {
                                        "task_type": "sfx",
                                        "name": "Скрип двери",
                                        "task_data": {
                                            "text": "Heavy wooden door creaks open and slams shut",
                                            "duration_seconds": 4,
                                            "prompt_influence": 0.3,
                                            "output_format": "mp3_44100_128"
                                        }
                                    }
                                },
                                "music": {
                                    "summary": "Музыка без шаблона",
                                    "description": "Музыкальный трек. n_variants умножает и цену, и число файлов в result.files[].",
                                    "value": {
                                        "task_type": "music",
                                        "name": "Фоновая тема",
                                        "task_data": {
                                            "prompt": "Calm lo-fi hip hop with soft piano and vinyl crackle",
                                            "n_variants": 2,
                                            "music_length_ms": 30000,
                                            "force_instrumental": true
                                        }
                                    }
                                },
                                "payg_confirm": {
                                    "summary": "Подтверждение PAYG-добора",
                                    "description": "Только в режиме отката к явному подтверждению: повтор того же заказа после ответа 402 payg_topup_required — согласие доплатить остаток из LMC вместе со свежим quote_token (живёт ~60 секунд). В норме добор списывается автоматически.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Здравствуйте! Рады видеть вас в Lumean.",
                                        "confirm_payg_topup": true,
                                        "quote_token": "eyJ1Ijo0MiwicyI6MSwidCI6MTIwMCwibSI6MzUwMCwiZSI6MTc1NDMwMDAwMH0.9f2c…"
                                    }
                                }
                            }
                        },
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/StoreOrderRequest",
                                "example": {
                                    "template_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                    "task_type": "tts",
                                    "name": "string",
                                    "task_data": [],
                                    "config_override": [],
                                    "skip_service_defaults": true,
                                    "input_text": "string",
                                    "confirm_payg_topup": true,
                                    "quote_token": "string",
                                    "input_files": [
                                        "<binary>"
                                    ]
                                }
                            },
                            "examples": {
                                "tts_template": {
                                    "summary": "TTS по шаблону",
                                    "description": "Обычный путь озвучки: настройки голоса и модели лежат в шаблоне, в заказе — только текст.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Здравствуйте! Рады видеть вас в Lumean.",
                                        "name": "Приветствие для лендинга"
                                    }
                                },
                                "tts_paragraph": {
                                    "summary": "TTS по абзацам",
                                    "description": "Абзацный режим: текст делится по пустой строке, абзац становится единицей генерации. В ответе появятся paragraphs[]. Минимальный тарифицируемый объём считается по КАЖДОМУ абзацу — короткие абзацы дороже, чем тот же текст сплошняком.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Первый абзац — вступление, его озвучиваем отдельно.\n\nВторой абзац продолжает мысль и тоже идёт своей единицей.\n\nТретий абзац завершает фрагмент.",
                                        "name": "Глава 1 — по абзацам",
                                        "config_override": {
                                            "generation_mode": "paragraph"
                                        }
                                    }
                                },
                                "tts_override": {
                                    "summary": "TTS другим голосом",
                                    "description": "Тот же шаблон, но разово другим голосом — через config_override, без создания нового шаблона.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Здравствуйте! Рады видеть вас в Lumean.",
                                        "config_override": {
                                            "tts_settings": {
                                                "voice_id": "EXAVITQu4vr4xnSDxMaL",
                                                "advanced_voice_settings": true,
                                                "voice_settings": {
                                                    "stability": 0.5,
                                                    "similarity_boost": 0.75
                                                }
                                            }
                                        }
                                    }
                                },
                                "sfx": {
                                    "summary": "SFX без шаблона",
                                    "description": "Звуковой эффект: шаблон не нужен, все параметры в task_data. Одна генерация = один файл.",
                                    "value": {
                                        "task_type": "sfx",
                                        "name": "Скрип двери",
                                        "task_data": {
                                            "text": "Heavy wooden door creaks open and slams shut",
                                            "duration_seconds": 4,
                                            "prompt_influence": 0.3,
                                            "output_format": "mp3_44100_128"
                                        }
                                    }
                                },
                                "music": {
                                    "summary": "Музыка без шаблона",
                                    "description": "Музыкальный трек. n_variants умножает и цену, и число файлов в result.files[].",
                                    "value": {
                                        "task_type": "music",
                                        "name": "Фоновая тема",
                                        "task_data": {
                                            "prompt": "Calm lo-fi hip hop with soft piano and vinyl crackle",
                                            "n_variants": 2,
                                            "music_length_ms": 30000,
                                            "force_instrumental": true
                                        }
                                    }
                                },
                                "payg_confirm": {
                                    "summary": "Подтверждение PAYG-добора",
                                    "description": "Только в режиме отката к явному подтверждению: повтор того же заказа после ответа 402 payg_topup_required — согласие доплатить остаток из LMC вместе со свежим quote_token (живёт ~60 секунд). В норме добор списывается автоматически.",
                                    "value": {
                                        "template_id": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c",
                                        "input_text": "Здравствуйте! Рады видеть вас в Lumean.",
                                        "confirm_payg_topup": true,
                                        "quote_token": "eyJ1Ijo0MiwicyI6MSwidCI6MTIwMCwibSI6MzUwMCwiZSI6MTc1NDMwMDAwMH0.9f2c…"
                                    }
                                }
                            }
                        }
                    },
                    "description": "Заказ создаётся одним из двух способов: **по шаблону** (`template_id` + `input_text`) или\n**без шаблона** (`task_type` + `task_data`). Выберите готовый пример ниже — он подставится\nв редактор."
                },
                "responses": {
                    "201": {
                        "description": "Заказ создан и поставлен в очередь. В `data` — заказ со статусом `created`/`pending`; результата в нём ещё нет.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Order",
                                            "description": "Созданный заказ. Он уже в очереди: `status` = `pending`, `result` пуст. Сохраните `id` и следите за статусом — опросом или по WebSocket."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order created successfully.",
                                        "data": {
                                            "id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
                                            "user_id": 501,
                                            "template_id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                            "task_type": "tts",
                                            "status": "created",
                                            "price_units": 420,
                                            "total_tokens": 0,
                                            "task_data": {
                                                "text": "Welcome to our platform! This narration greets every new listener.",
                                                "total_length": 420
                                            },
                                            "result": null,
                                            "created_at": "2026-07-17T15:00:00+00:00",
                                            "updated_at": "2026-07-17T15:00:00+00:00",
                                            "eta_seconds": null,
                                            "eta_at": null,
                                            "completed_chunks": 0,
                                            "total_chunks": 2,
                                            "progress_percent": 0
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка валидации. Сюда же попадает `reason: content_blocked` — фрагмент уже отклонялся контентной политикой и занесён в блоклист, и `reason: paragraph_not_voiceable` — в тексте есть фрагмент из одних пауз, служебных тегов или ремарок, озвучивать в нём нечего. Отдельный случай — `reason: text_empty`: озвучивать нечего вовсе, потому что текста в заказе нет ни одним из каналов (`input_text`, загруженный файл, `task_data.text`) либо в присланном не нашлось ни одного произносимого фрагмента. Повтор такого запроса даст тот же отказ — исправьте текст. Когда текста нет уже на входе, поле перечислено в `errors` и `reason` не приходит. Ещё один случай — `reason: template_model_outdated`: шаблон или `config_override` несут настройки прежней схемы сервиса (модель или движок, которых в текущей схеме нет), пути этих настроек — в поле `fields`. Тихого перевода на новую схему нет: выберите модель в шаблоне заново (`PUT /templates/{template}`) и повторите запрос.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Ошибка создания",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The request could not be processed."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Не хватает денег на часть заказа сверх квоты подписки (`reason: insufficient_balance`) либо тариф запрещает добор (`insufficient_allowance`). Добор, который покрывает баланс, списывается автоматически, и заказ создаётся с `201`. Только в режиме отката к явному подтверждению: `payg_topup_required` — тело несёт `shortfall_tokens`, `shortfall_lmc`, `quote_token` и `expires_at`, повторите запрос с `confirm_payg_topup: true` и этим токеном. Различайте случаи по полю `reason`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "enum": [
                                                "insufficient_balance",
                                                "insufficient_allowance",
                                                "payg_topup_required"
                                            ],
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "enum": [
                                                "insufficient_balance",
                                                "insufficient_allowance",
                                                "payg_topup_required"
                                            ],
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "shortfall_tokens": {
                                            "type": "integer",
                                            "example": 1200,
                                            "description": "Сколько токенов не покрыла квота подписки — именно они оплачиваются из LMC."
                                        },
                                        "shortfall_lmc": {
                                            "type": "number",
                                            "example": 3.6,
                                            "description": "Стоимость доплаты в LMC, готовая к показу пользователю."
                                        },
                                        "shortfall_lmc_minor": {
                                            "type": "integer",
                                            "example": 360000000,
                                            "description": "Та же сумма в минимальных единицах LMC — для точных расчётов без потери копеек."
                                        },
                                        "quote_token": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "example": "q_9f8b2c1a4d3e",
                                            "description": "Подписанная котировка цены: верните её без изменений в повторном запросе вместе с `confirm_payg_topup: true`."
                                        },
                                        "expires_at": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "format": "date-time",
                                            "example": "2026-07-25T12:34:56+00:00",
                                            "description": "До этого момента цена зафиксирована. Позже — повторите запрос и получите новую котировку."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Insufficient balance to cover the part of the order beyond the subscription quota.",
                                        "reason": "insufficient_balance",
                                        "error_code": "insufficient_balance"
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права orders.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Только в режиме отката к явному подтверждению: котировка добора устарела или подорожала (`reason: quote_mismatch`) — параллельный заказ съел квоту. Повторите запрос без подтверждения, получите новый `402` и новую сумму.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "enum": [
                                                "quote_mismatch"
                                            ],
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "enum": [
                                                "quote_mismatch"
                                            ],
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "actual_lmc_minor": {
                                            "type": "integer",
                                            "example": 380000000,
                                            "description": "Во сколько добор обходится сейчас (минимальные единицы LMC)."
                                        },
                                        "quoted_lmc_minor": {
                                            "type": "integer",
                                            "example": 360000000,
                                            "description": "Цена, на которую вы согласились ранее (минимальные единицы LMC)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code",
                                        "actual_lmc_minor",
                                        "quoted_lmc_minor"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The pay-as-you-go quote is stale — request a fresh one.",
                                        "reason": "quote_mismatch",
                                        "error_code": "quote_mismatch",
                                        "actual_lmc_minor": 380000000,
                                        "quoted_lmc_minor": 360000000
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышена токен-квота ключа или исчерпаны слоты одновременных задач. Отдельный случай — `reason: policy_cooldown_active`: кулдаун после серии блокировок по контентной политике, время в `retry_after`/`retry_at` и в заголовке `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/chunks/preview": {
            "post": {
                "operationId": "order.previewChunks",
                "description": "Dry-run разбиения текста на чанки и расчёта стоимости — БЕЗ создания заказа. Тот же\nрасчёт, что использует `POST /orders`: сверяйте здесь разбиение/цену перед реальной\nотправкой. **Не тратит токен-квоту ключа** — preview бесплатен.\n\nЕдиный ready-конверт для sync и async путей:\n\n- **200** `status=ready` — TTS-текст ≤ 50 000 символов (и любой non-TTS): расчёт\n  готов сразу, тело содержит `summary` (итоги по всему тексту) + первую страницу\n  `chunks[]` + `page` (мета пагинации).\n- **202** `status=processing` — TTS-текст длиннее порога: расчёт уходит в фон,\n  тело содержит только `preview_id`. Результат приходит WS-событием `.ready` на\n  канал `private-chunks.preview.{userId}` (авторизация — `POST /broadcasting/auth`,\n  как и для остальных приватных каналов), либо подтяните вручную\n  `GET /orders/chunks/preview/{previewId}`.\n\n**Инкрементальный re-preview:** передайте `preview_id` предыдущего ответа как\n`previous_preview_id` вместе с новым `input_text` — пересчитается только\nокрестность правки, результат идентичен полному расчёту.\n\n**Настройки заказа:** передайте тот же `config_override`, что уйдёт в `POST /orders`, —\nпревью режет по конфигу шаблона с ним, как заказ (настройки, влияющие на рез, действуют\nи здесь). Значение, которое создание заказа отбило бы `422`, превью тоже отбивает.\n\n**Паузы/капы:** превышение лимитов пауз (>500 шт., >3600с суммарно, >10с одна) или\nразмера заказа (>1500 фрагментов) НЕ роняет запрос — приходит `summary.caps_ok=false`\n+ `caps_exceeded`. Это отличает preview от `POST /orders`, где то же превышение\nдаёт `422`.\n\nКэш превью общий с веб-сессией того же аккаунта (если владелец ключа пользуется и\nсайтом): одинаковый текст+шаблон+режим, уже посчитанный в браузере, вернётся мгновенно.\n\n**Требуемое право ключа:** `orders.read` (preview не мутирует данные — логически чтение).",
                "summary": "Preview Chunk Split",
                "tags": [
                    "Public Orders"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/PreviewChunksRequest",
                                "example": {
                                    "template_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                    "task_type": "string",
                                    "input_text": "string",
                                    "previous_preview_id": "string",
                                    "text_hash": "string",
                                    "generation_mode": "default",
                                    "config_override": []
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "status=ready: summary + первая страница чанков + мета пагинации",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "status": {
                                                    "type": "string",
                                                    "enum": [
                                                        "ready"
                                                    ],
                                                    "description": "Всегда `ready` у кода 200 — расчёт готов."
                                                },
                                                "preview_id": {
                                                    "type": "string",
                                                    "description": "Контентный идентификатор превью (sha256 от текста+шаблона+режима+версии алгоритма). Используйте для инкрементального re-preview (`previous_preview_id`) и для пагинации (`GET .../{previewId}`)."
                                                },
                                                "summary": {
                                                    "type": "object",
                                                    "properties": {
                                                        "chunks_count": {
                                                            "type": "integer",
                                                            "description": "Сколько всего чанков в разбиении."
                                                        },
                                                        "total_effective_length": {
                                                            "type": "integer",
                                                            "description": "Суммарная billable-длина по всем чанкам, в символах (паузы и указания диктору не считаются)."
                                                        },
                                                        "total_price_units": {
                                                            "type": "integer",
                                                            "description": "Суммарная стоимость в токенах — та же величина, что спишет `POST /orders` за идентичный текст."
                                                        },
                                                        "voiced_length": {
                                                            "type": "integer",
                                                            "description": "Фактически озвучиваемая длина ДО добора до минимального тарифицируемого объёма — для честного показа «сколько символов»."
                                                        },
                                                        "min_billable_chars": {
                                                            "type": "integer",
                                                            "description": "Минимальный тарифицируемый объём (0 — минимума нет). Если `voiced_length` меньше этого порога, стоимость всё равно посчитана по порогу."
                                                        },
                                                        "pause_count": {
                                                            "type": "integer",
                                                            "description": "Число пауз в тексте (`{{pause}}`/`<break/>`). Паузы бесплатны — в стоимость не входят."
                                                        },
                                                        "total_pause_seconds": {
                                                            "type": "number",
                                                            "description": "Суммарная длительность тишины, секунды."
                                                        },
                                                        "caps_ok": {
                                                            "type": "boolean",
                                                            "description": "Санитарные капы заказа не превышены. `false` НЕ роняет превью (в отличие от `POST /orders`, где то же самое даёт `422`) — только предупреждает."
                                                        },
                                                        "caps_exceeded": {
                                                            "type": [
                                                                "string",
                                                                "null"
                                                            ],
                                                            "description": "Какой именно кап превышен: `count` (пауз больше 500), `single` (одна пауза длиннее 10с), `total` (суммарная тишина больше 3600с) или `chunks` (заказ даёт больше 1500 фрагментов — столько система не соберёт в один файл). `null` — капы в порядке."
                                                        },
                                                        "voiced_empty": {
                                                            "type": "boolean",
                                                            "description": "В тексте не осталось ничего произносимого (только теги/паузы/скобочные ремарки). `POST /orders` с таким текстом вернёт `422`."
                                                        },
                                                        "blocked_chunks": {
                                                            "type": "array",
                                                            "items": {
                                                                "type": "integer"
                                                            },
                                                            "description": "Индексы чанков, чей текст уже отбивался контентной политикой и лежит в блоклисте. Пустой массив — блокировок нет."
                                                        },
                                                        "has_blocked_content": {
                                                            "type": "boolean",
                                                            "description": "Есть ли хотя бы один заблокированный чанк — то же самое, что `blocked_chunks !== []`, в булевом виде для удобства."
                                                        },
                                                        "paragraphs": {
                                                            "type": [
                                                                "array",
                                                                "null"
                                                            ],
                                                            "items": {
                                                                "type": "object",
                                                                "properties": {
                                                                    "paragraph_index": {
                                                                        "type": "integer",
                                                                        "description": "Порядковый номер абзаца, с нуля."
                                                                    },
                                                                    "chunks_count": {
                                                                        "type": "integer",
                                                                        "description": "Сколько чанков внутри этого абзаца (длинный абзац может занять несколько чанков)."
                                                                    },
                                                                    "first_chunk_index": {
                                                                        "type": "integer",
                                                                        "description": "Сквозной `index` первого чанка абзаца — по нему абзац находится в постраничном `data.chunks`, даже если его чанки попали на другую страницу."
                                                                    },
                                                                    "text_length": {
                                                                        "type": "integer",
                                                                        "description": "Сырая длина текста абзаца в символах, включая теги/паузы."
                                                                    },
                                                                    "effective_length": {
                                                                        "type": "integer",
                                                                        "description": "Billable-длина абзаца — минимальный тарифицируемый объём в абзацном режиме считается ПО КАЖДОМУ абзацу отдельно, поэтому короткие абзацы могут стоить дороже, чем тот же текст, разрезанный сплошняком."
                                                                    },
                                                                    "price_units": {
                                                                        "type": "integer",
                                                                        "description": "Стоимость абзаца в токенах."
                                                                    }
                                                                },
                                                                "required": [
                                                                    "paragraph_index",
                                                                    "chunks_count",
                                                                    "first_chunk_index",
                                                                    "text_length",
                                                                    "effective_length",
                                                                    "price_units"
                                                                ]
                                                            },
                                                            "description": "`null` — превью не абзацное (то же значение `generation_mode`, что и в заказе, отвечает за это поле). В абзацном режиме — агрегат по каждому абзацу."
                                                        },
                                                        "paragraphs_count": {
                                                            "type": [
                                                                "integer",
                                                                "null"
                                                            ],
                                                            "description": "Число абзацев, или `null` вне абзацного режима. Остаётся числом даже когда `paragraphs` схлопнут по WS-капу."
                                                        },
                                                        "cost_lmc": {
                                                            "type": "number",
                                                            "description": "Стоимость в LMC (валюто-независимая база). Поле может ОТСУТСТВОВАТЬ (не `null`), если денежный якорь не удалось посчитать (best-effort, не критично для превью)."
                                                        },
                                                        "cost_display": {
                                                            "type": [
                                                                "object",
                                                                "null"
                                                            ],
                                                            "description": "Стоимость, пересчитанная в валюту отображения аккаунта — та же мультивалютная разбивка, что и в остальном публичном API (`amount`/`amount_formatted` по коду валюты). Поле может ОТСУТСТВОВАТЬ, если `cost_lmc` не посчитан или курс недоступен."
                                                        }
                                                    },
                                                    "required": [
                                                        "chunks_count",
                                                        "total_effective_length",
                                                        "total_price_units",
                                                        "voiced_length",
                                                        "min_billable_chars",
                                                        "pause_count",
                                                        "total_pause_seconds",
                                                        "caps_ok",
                                                        "caps_exceeded",
                                                        "voiced_empty",
                                                        "blocked_chunks",
                                                        "has_blocked_content",
                                                        "paragraphs",
                                                        "paragraphs_count"
                                                    ],
                                                    "description": "Итоги по всему тексту — не только по текущей странице чанков."
                                                },
                                                "chunks": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "index": {
                                                                "type": "integer",
                                                                "description": "Сквозной номер чанка по всему тексту (не сбрасывается на границах абзацев)."
                                                            },
                                                            "text": {
                                                                "type": "string",
                                                                "description": "Текст чанка ровно в том виде, в каком его получит voice-провайдер."
                                                            },
                                                            "start": {
                                                                "type": "integer",
                                                                "description": "Начало чанка в НОРМАЛИЗОВАННОМ входном тексте (символьное смещение). Нормализация — та же, что применяет `POST /orders` перед резом."
                                                            },
                                                            "end": {
                                                                "type": "integer",
                                                                "description": "Конец чанка в нормализованном тексте (полуинтервал: `end` не входит в чанк)."
                                                            },
                                                            "text_length": {
                                                                "type": "integer",
                                                                "description": "Сырая длина текста чанка в символах, ВКЛЮЧАЯ теги/паузы."
                                                            },
                                                            "effective_length": {
                                                                "type": "integer",
                                                                "description": "Billable-длина чанка (без пауз, после добора до минимума при его наличии) — сумма по всем чанкам страницы не обязана совпадать с `total_effective_length` (та считается по ВСЕМ чанкам, не только странице)."
                                                            },
                                                            "price_units": {
                                                                "type": "integer",
                                                                "description": "Стоимость чанка в токенах."
                                                            },
                                                            "paragraph_index": {
                                                                "type": [
                                                                    "integer",
                                                                    "null"
                                                                ],
                                                                "description": "Номер абзаца, которому принадлежит чанк, или `null` вне абзацного режима."
                                                            }
                                                        },
                                                        "required": [
                                                            "index",
                                                            "text",
                                                            "start",
                                                            "end",
                                                            "text_length",
                                                            "effective_length",
                                                            "price_units",
                                                            "paragraph_index"
                                                        ]
                                                    },
                                                    "description": "Только ТЕКУЩАЯ страница (см. `data.page`), не весь список — используйте пагинацию (`GET .../{previewId}?page=`) для остальных."
                                                },
                                                "page": {
                                                    "type": "object",
                                                    "properties": {
                                                        "current_page": {
                                                            "type": "integer",
                                                            "description": "Номер текущей страницы, считая с 1."
                                                        },
                                                        "per_page": {
                                                            "type": "integer",
                                                            "description": "Размер страницы (сколько чанков запрошено/использовано по умолчанию)."
                                                        },
                                                        "total": {
                                                            "type": "integer",
                                                            "description": "Всего чанков в разбиении (по всем страницам)."
                                                        },
                                                        "has_more": {
                                                            "type": "boolean",
                                                            "description": "Есть ли ещё страницы после текущей."
                                                        }
                                                    },
                                                    "required": [
                                                        "current_page",
                                                        "per_page",
                                                        "total",
                                                        "has_more"
                                                    ],
                                                    "description": "Мета пагинации ТЕКУЩЕЙ страницы `data.chunks`."
                                                }
                                            },
                                            "required": [
                                                "status",
                                                "preview_id",
                                                "summary",
                                                "chunks",
                                                "page"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": "string"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка валидации (input_text обязателен) или reason=template_model_outdated — шаблон или config_override несут настройки прежней схемы сервиса, как у создания заказа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "202": {
                        "description": "status=processing: расчёт в фоне, вернётся preview_id (результат — WS .ready)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "status": {
                                                    "type": "string",
                                                    "enum": [
                                                        "processing"
                                                    ],
                                                    "description": "Всегда `processing` у кода 202 — расчёт идёт в фоне."
                                                },
                                                "preview_id": {
                                                    "type": "string",
                                                    "description": "Идентификатор превью. Используйте для подписки на WS-канал `private-chunks.preview.{userId}` (событие `.ready`) либо для ручного поллинга `GET /orders/chunks/preview/{previewId}`."
                                                }
                                            },
                                            "required": [
                                                "status",
                                                "preview_id"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": "string"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права orders.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышен лимит запросов preview для этого ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "get": {
                "operationId": "order.lookupPreview",
                "description": "Preflight: есть ли уже готовое разбиение этого текста в кэше — **без пересылки\nсамого текста**. Пришлите `text_hash` (sha256 сырого `input_text`, нижний регистр\nhex) + `template_id` (+ `generation_mode` и `config_override`, если использовались при расчёте) —\nсервер соберёт тот же `preview_id` и проверит кэш.\n\n- **200** `status=ready` — уже посчитано, текст слать не нужно.\n- **202** `status=processing` — расчёт этого текста ещё идёт.\n- **404** — в кэше нет → отправьте полный текст через `POST /orders/chunks/preview`.\n\n**Требуемое право ключа:** `orders.read`.",
                "summary": "Preview Lookup",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "template_id",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID шаблона — тот же, что был указан в `POST /orders/chunks/preview` при расчёте. Часть идентичности `preview_id`.",
                        "example": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c"
                    },
                    {
                        "name": "text_hash",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^[a-f0-9]{64}$"
                        },
                        "description": "sha256 от СЫРОГО `input_text` (нижний регистр hex, 64 символа) — считайте у себя,\nнормализацию бэк делает сам и в хеш её не примешивает. Вместе с `template_id` и\n`generation_mode` детерминирует `preview_id`: та же тройка → тот же ответ, без\nпересылки самого текста.",
                        "example": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
                    },
                    {
                        "name": "generation_mode",
                        "in": "query",
                        "description": "Режим генерации, если использовался при расчёте (`paragraph` — абзацный рез). Обязан совпадать с тем, что уходило в `POST` — режим входит в идентичность `preview_id`.",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "enum": [
                                "default",
                                "paragraph"
                            ]
                        },
                        "example": "paragraph"
                    },
                    {
                        "name": "config_override[]",
                        "in": "query",
                        "schema": {
                            "type": [
                                "array",
                                "null"
                            ],
                            "items": {
                                "type": "string"
                            }
                        },
                        "description": "`config_override`, с которым превью считали в `POST` (если передавали). Настройки,\nвлияющие на рез, входят в идентичность `preview_id`, остальные — нет. Передаётся\nJSON-строкой (`config_override={\"tts_settings\":{\"mode\":\"mode_v1\"}}`, URL-кодированной)\nили вложенными ключами (`config_override[tts_settings][mode]=mode_v1`)."
                    }
                ],
                "responses": {
                    "404": {
                        "description": "В кэше нет — нужно отправить полный текст через POST",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "anyOf": [
                                                {
                                                    "type": "null"
                                                },
                                                {
                                                    "type": "array",
                                                    "items": {}
                                                },
                                                {
                                                    "type": "string"
                                                },
                                                {
                                                    "type": "object"
                                                }
                                            ],
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "202": {
                        "description": "status=processing: расчёт ещё идёт",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "status": {
                                                    "type": "string",
                                                    "description": "Всегда `processing` у кода 202 — расчёт идёт в фоне."
                                                },
                                                "preview_id": {
                                                    "type": "string",
                                                    "description": "Идентификатор превью. Используйте для подписки на WS-канал `private-chunks.preview.{userId}` (событие `.ready`) либо для ручного поллинга `GET /orders/chunks/preview/{previewId}`."
                                                },
                                                "summary": {
                                                    "type": "string",
                                                    "description": "Итоги по всему тексту — не только по текущей странице чанков."
                                                },
                                                "chunks": {
                                                    "type": "array",
                                                    "items": {},
                                                    "description": "Только ТЕКУЩАЯ страница (см. `data.page`), не весь список — используйте пагинацию (`GET .../{previewId}?page=`) для остальных."
                                                },
                                                "page": {
                                                    "type": "string",
                                                    "description": "Мета пагинации ТЕКУЩЕЙ страницы `data.chunks`."
                                                }
                                            },
                                            "required": [
                                                "status",
                                                "preview_id",
                                                "summary",
                                                "chunks",
                                                "page"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "status": "string",
                                            "preview_id": "string",
                                            "summary": "string",
                                            "chunks": [
                                                "string"
                                            ],
                                            "page": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "200": {
                        "description": "status=ready: summary + первая страница чанков",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "status": {
                                                    "type": "string",
                                                    "description": "Всегда `ready` у кода 200 — расчёт готов."
                                                },
                                                "preview_id": {
                                                    "type": "string",
                                                    "description": "Контентный идентификатор превью (sha256 от текста+шаблона+режима+версии алгоритма). Используйте для инкрементального re-preview (`previous_preview_id`) и для пагинации (`GET .../{previewId}`)."
                                                },
                                                "summary": {
                                                    "type": "string",
                                                    "description": "Итоги по всему тексту — не только по текущей странице чанков."
                                                },
                                                "chunks": {
                                                    "type": "array",
                                                    "items": {},
                                                    "description": "Только ТЕКУЩАЯ страница (см. `data.page`), не весь список — используйте пагинацию (`GET .../{previewId}?page=`) для остальных."
                                                },
                                                "page": {
                                                    "type": "string",
                                                    "description": "Мета пагинации ТЕКУЩЕЙ страницы `data.chunks`."
                                                }
                                            },
                                            "required": [
                                                "status",
                                                "preview_id",
                                                "summary",
                                                "chunks",
                                                "page"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "status": "string",
                                            "preview_id": "string",
                                            "summary": "string",
                                            "chunks": [
                                                "string"
                                            ],
                                            "page": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка валидации",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права orders.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышен лимит запросов чтения preview для этого ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/chunks/preview/{previewId}": {
            "get": {
                "operationId": "order.getPreviewPage",
                "description": "Страница чанков уже посчитанного preview из кэша (без пересчёта) — пагинация по\n`preview_id`, полученному из `POST`/`GET /orders/chunks/preview`.\n\n- **200** `status=ready` — страница чанков + мета пагинации. Страница вне диапазона\n  → пустой `chunks[]` + `has_more=false`.\n- **202** `status=processing` — расчёт ещё идёт.\n- **404** — preview не найден, протух (TTL ~30 мин) или расчёт упал — пере-отправьте\n  через `POST /orders/chunks/preview`.\n\n**Требуемое право ключа:** `orders.read`.",
                "summary": "Preview Page",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "previewId",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Идентификатор превью — sha256-хеш (64 символа нижнего регистра, БЕЗ дефисов), не UUID.\n\nБерётся из поля `preview_id` ответа `POST`/`GET /orders/chunks/preview`. Чужой\n`preview_id` вернёт `404` — кэш изолирован по владельцу ключа.",
                        "example": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": [
                                "integer",
                                "null"
                            ],
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": [
                                "integer",
                                "null"
                            ],
                            "minimum": 1,
                            "maximum": 500
                        },
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "example": 20
                    }
                ],
                "responses": {
                    "404": {
                        "description": "Preview не найден или протух",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "anyOf": [
                                                {
                                                    "type": "null"
                                                },
                                                {
                                                    "type": "array",
                                                    "items": {}
                                                },
                                                {
                                                    "type": "string"
                                                },
                                                {
                                                    "type": "object"
                                                }
                                            ],
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "200": {
                        "description": "status=ready: страница чанков + мета пагинации",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "status": {
                                                    "type": "string",
                                                    "description": "Всегда `ready` у кода 200 — расчёт готов."
                                                },
                                                "preview_id": {
                                                    "type": "string",
                                                    "description": "Контентный идентификатор превью (sha256 от текста+шаблона+режима+версии алгоритма). Используйте для инкрементального re-preview (`previous_preview_id`) и для пагинации (`GET .../{previewId}`)."
                                                },
                                                "summary": {
                                                    "type": "string",
                                                    "description": "Итоги по всему тексту — не только по текущей странице чанков."
                                                },
                                                "chunks": {
                                                    "type": "array",
                                                    "items": {},
                                                    "description": "Только ТЕКУЩАЯ страница (см. `data.page`), не весь список — используйте пагинацию (`GET .../{previewId}?page=`) для остальных."
                                                },
                                                "page": {
                                                    "type": "string",
                                                    "description": "Мета пагинации ТЕКУЩЕЙ страницы `data.chunks`."
                                                }
                                            },
                                            "required": [
                                                "status",
                                                "preview_id",
                                                "summary",
                                                "chunks",
                                                "page"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "status": "string",
                                            "preview_id": "string",
                                            "summary": "string",
                                            "chunks": [
                                                "string"
                                            ],
                                            "page": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "202": {
                        "description": "status=processing: расчёт ещё идёт",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "status": {
                                                    "type": "string",
                                                    "description": "Всегда `processing` у кода 202 — расчёт идёт в фоне."
                                                },
                                                "preview_id": {
                                                    "type": "string",
                                                    "description": "Идентификатор превью. Используйте для подписки на WS-канал `private-chunks.preview.{userId}` (событие `.ready`) либо для ручного поллинга `GET /orders/chunks/preview/{previewId}`."
                                                },
                                                "summary": {
                                                    "type": "string",
                                                    "description": "Итоги по всему тексту — не только по текущей странице чанков."
                                                },
                                                "chunks": {
                                                    "type": "array",
                                                    "items": {},
                                                    "description": "Только ТЕКУЩАЯ страница (см. `data.page`), не весь список — используйте пагинацию (`GET .../{previewId}?page=`) для остальных."
                                                },
                                                "page": {
                                                    "type": "string",
                                                    "description": "Мета пагинации ТЕКУЩЕЙ страницы `data.chunks`."
                                                }
                                            },
                                            "required": [
                                                "status",
                                                "preview_id",
                                                "summary",
                                                "chunks",
                                                "page"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "status": "string",
                                            "preview_id": "string",
                                            "summary": "string",
                                            "chunks": [
                                                "string"
                                            ],
                                            "page": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права orders.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышен лимит запросов чтения preview для этого ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}": {
            "get": {
                "operationId": "order.show",
                "description": "Возвращает детальную информацию о заказе владельца ключа.\n\n**Включает:**\n- Статус и результат выполнения (`result.files[]` — основные файлы)\n- `result.service_files[]` — сервисные файлы (субтитры `.srt`/`.vtt`/`.lrc`,\n  alignment) для TTS-заказов; отдельный плоский массив строк-путей, качается\n  через `POST /storage/url`, как и основные файлы. У `sfx`/`music` отсутствует.\n- Использованный шаблон и сервис. `template.config` — настройки, с которыми выполняется\n  этот заказ: конфиг шаблона на момент создания с наложенным `config_override`; правка\n  или удаление шаблона их не меняют (у заказов, созданных до конца сентября 2026 года,\n  снимка нет — там текущий конфиг шаблона с `config_override`)\n- `items[]` — чанки заказа\n\n> У упавшего заказа (`failed`/`compensated`) `result` приходит `null` — деталей\n> ошибки в этом контексте нет. `result.user_message` присутствует редко (обычно\n> `null`): это passthrough-поле от воркера, а не гарантированный текст статуса.\n\n**Требуемое право ключа:** `orders.read`.",
                "summary": "Get Order",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Информация о заказе",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Order",
                                            "description": "Заказ целиком, вместе с чанками в `items` и путями к файлам в `result`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order loaded successfully.",
                                        "data": {
                                            "id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                            "user_id": 501,
                                            "template": {
                                                "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                                "user_id": 501,
                                                "slug": null,
                                                "name": "Narrator Voice (EN)",
                                                "service_key": "elevenlabs",
                                                "service": {
                                                    "id": 3,
                                                    "code": "elevenlabs",
                                                    "name": "ElevenLabs",
                                                    "display_name": "ElevenLabs TTS",
                                                    "payg_access": "everyone",
                                                    "billing_mode": "pay_as_you_go",
                                                    "unit_asset_id": 5,
                                                    "unit_asset": {
                                                        "id": 5,
                                                        "code": "tts_units",
                                                        "kind": "unit",
                                                        "precision": 0,
                                                        "name": "TTS Units",
                                                        "deleted_at": null,
                                                        "created_at": "2026-01-10T09:00:00+00:00",
                                                        "updated_at": "2026-01-10T09:00:00+00:00"
                                                    },
                                                    "required_entitlement_id": null,
                                                    "units_per_call": null,
                                                    "units_per_char": 1,
                                                    "units_per_image": null,
                                                    "units_per_second": null,
                                                    "min_billable_chars": 1,
                                                    "lumcoin_per_token": 10,
                                                    "lumc_precision": 2,
                                                    "price_lumc_minor": null,
                                                    "unit_size": 1,
                                                    "uses_money_first_pricing": false,
                                                    "rate_unit": "char",
                                                    "display_category": "voice",
                                                    "is_premium": false,
                                                    "display_order": 1,
                                                    "metadata": null,
                                                    "is_active": true,
                                                    "created_at": "2026-01-10T09:00:00+00:00",
                                                    "updated_at": "2026-01-10T09:00:00+00:00"
                                                },
                                                "config": {
                                                    "tts_settings": {
                                                        "model_id": "eleven_multilingual_v2",
                                                        "voice_id": "21m00Tcm4TlvDq8ikWAM",
                                                        "mode": "mode_v1",
                                                        "language_code": null,
                                                        "advanced_voice_settings": true,
                                                        "voice_settings": {
                                                            "stability": 0.5,
                                                            "similarity_boost": 0.75,
                                                            "use_speaker_boost": true
                                                        }
                                                    }
                                                },
                                                "is_public": false,
                                                "folder_id": null,
                                                "created_at": "2026-06-01T10:00:00+00:00",
                                                "updated_at": "2026-06-01T10:00:00+00:00"
                                            },
                                            "template_id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                            "task_type": "tts",
                                            "status": "completed",
                                            "price_units": 420,
                                            "total_tokens": 420,
                                            "task_data": {
                                                "text": "Welcome to our platform! This narration greets every new listener.",
                                                "total_length": 420
                                            },
                                            "result": {
                                                "user_message": null,
                                                "files": [
                                                    "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/result.mp3"
                                                ],
                                                "service_files": [
                                                    "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/service/subtitles.srt",
                                                    "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/service/subtitles.vtt",
                                                    "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final/service/result.json"
                                                ]
                                            },
                                            "created_at": "2026-07-17T12:00:00+00:00",
                                            "updated_at": "2026-07-17T12:02:50+00:00",
                                            "eta_seconds": null,
                                            "eta_at": null,
                                            "completed_chunks": 2,
                                            "total_chunks": 2,
                                            "progress_percent": 100,
                                            "items": [
                                                {
                                                    "id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
                                                    "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                                    "item_type": "chunk",
                                                    "chunk_index": 0,
                                                    "parent_item_id": null,
                                                    "original_text_length": 240,
                                                    "current_text_length": 240,
                                                    "price_units": 240,
                                                    "consumed_units": 240,
                                                    "remaining_locked": 0,
                                                    "status": "completed",
                                                    "result_file": {
                                                        "path": "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/chunks/0/result.mp3",
                                                        "storage": "s3",
                                                        "name": "chunk_0.mp3",
                                                        "size": 512340,
                                                        "mime_type": "audio/mpeg",
                                                        "size_formatted": "500.33 KB"
                                                    },
                                                    "attempt_number": 1,
                                                    "can_retry": false,
                                                    "can_regenerate": true,
                                                    "created_at": "2026-07-17T12:00:05+00:00",
                                                    "updated_at": "2026-07-17T12:02:10+00:00"
                                                },
                                                {
                                                    "id": "e5f6a7b8-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                                                    "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                                    "item_type": "chunk",
                                                    "chunk_index": 1,
                                                    "parent_item_id": null,
                                                    "original_text_length": 180,
                                                    "current_text_length": 180,
                                                    "price_units": 180,
                                                    "consumed_units": 180,
                                                    "remaining_locked": 0,
                                                    "status": "completed",
                                                    "result_file": {
                                                        "path": "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/chunks/1/result.mp3",
                                                        "storage": "s3",
                                                        "name": "chunk_1.mp3",
                                                        "size": 398210,
                                                        "mime_type": "audio/mpeg",
                                                        "size_formatted": "388.88 KB"
                                                    },
                                                    "attempt_number": 1,
                                                    "can_retry": false,
                                                    "can_regenerate": true,
                                                    "created_at": "2026-07-17T12:00:05+00:00",
                                                    "updated_at": "2026-07-17T12:02:45+00:00"
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Заказ не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/cancel": {
            "post": {
                "operationId": "order.cancel",
                "description": "Отменяет заказ владельца ключа и возвращает заблокированные средства на баланс.\n\n**Условия:**\n- Заказ в статусе `created`, `pending` или `partially_completed`.\n\nВозврат токен-квоты ключа (если заказ создан по ключу) выполняется автоматически.\n\n**Требуемое право ключа:** `orders.write`.",
                "summary": "Cancel Order",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Заказ отменён",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Order",
                                            "description": "Заказ после отмены: `status` = `canceled`, зарезервированные средства и токен-квота уже возвращены."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order cancelled successfully.",
                                        "data": {
                                            "id": "e6f7a8b9-0c1d-4e2f-8a3b-4c5d6e7f8a9b",
                                            "user_id": 501,
                                            "template_id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                            "task_type": "tts",
                                            "status": "cancelled",
                                            "price_units": 300,
                                            "total_tokens": 0,
                                            "task_data": {
                                                "text": "A shorter narration cancelled before processing started.",
                                                "total_length": 300
                                            },
                                            "result": null,
                                            "created_at": "2026-07-17T13:00:00+00:00",
                                            "updated_at": "2026-07-17T13:05:00+00:00",
                                            "eta_seconds": null,
                                            "eta_at": null,
                                            "completed_chunks": 0,
                                            "total_chunks": 2,
                                            "progress_percent": 0
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Невозможно отменить заказ в текущем статусе: reason=invalid_order_status",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "An order with the current status cannot be cancelled.",
                                        "reason": "invalid_order_status",
                                        "error_code": "invalid_order_status"
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу или у ключа нет права orders.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Заказ не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/retry": {
            "post": {
                "operationId": "order.retry",
                "description": "Создаёт НОВЫЙ заказ с теми же параметрами (полный retry завершённого заказа).\n\n**Условия:**\n- Исходный заказ в статусе `completed` или `result_delivered`, без `failed`-чанков.\n\n**Настройки нового заказа** — снимок ИСХОДНОГО: конфиг шаблона на момент его создания и\nтот же `config_override`, а не текущее состояние шаблона; правка и удаление шаблона на\nповтор не влияют. Заказы, созданные до конца сентября 2026 года, снимка не имеют и\nповторяются по текущему шаблону. Если сохранённые настройки больше не проходят текущую\nсхему сервиса — `400` с `reason: retry_config_outdated`: создайте заказ заново через\n`POST /orders`.\n\n> Это НЕ способ дотянуть `partially_completed`-заказ: такой статус здесь не\n> принимается. Частично выполненный заказ доводят поштучно — item-level\n> `POST /orders/{order}/items/{item}/retry` по каждому `policy_flagged`/`failed` чанку.\n\n**Биллинг:** это полностью новый заказ с новой блокировкой средств. Новый заказ\nпривязывается к вашему API-ключу, его стоимость учитывается в\nтокен-квоте ключа (pre-check → `429` при превышении, заказ не создаётся).\n\n**Требуемое право ключа:** `orders.write`.",
                "summary": "Retry Order",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/RetryOrderRequest",
                                "example": {
                                    "confirm_payg_topup": true,
                                    "quote_token": "string"
                                }
                            },
                            "examples": {
                                "plain": {
                                    "summary": "Обычный повтор",
                                    "description": "Создаёт новый заказ с теми же параметрами. Тело пустое.",
                                    "value": {}
                                },
                                "payg_confirm": {
                                    "summary": "После 402 — с подтверждением",
                                    "description": "Только режим отката: повтор с согласием доплатить остаток из LMC.",
                                    "value": {
                                        "confirm_payg_topup": true,
                                        "quote_token": "eyJ1Ijo0MiwicyI6MSwidCI6MTIwMCwibSI6MzUwMCwiZSI6MTc1NDMwMDAwMH0.9f2c…"
                                    }
                                }
                            }
                        }
                    },
                    "description": "Тело не обязательно. Поля нужны только для подтверждения PAYG-добора после ответа `402`\nв режиме отката к явному подтверждению; в норме добор списывается автоматически."
                },
                "responses": {
                    "201": {
                        "description": "Создан **новый** заказ с параметрами исходного. Исходный заказ не меняется.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Order",
                                            "description": "**Новый** заказ с теми же параметрами — у него свой `id`. Исходный заказ не меняется."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order retry created successfully.",
                                        "data": {
                                            "id": "d5e6f7a8-9b0c-4d1e-8f2a-3b4c5d6e7f8a",
                                            "user_id": 501,
                                            "template_id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                            "task_type": "tts",
                                            "status": "created",
                                            "price_units": 420,
                                            "total_tokens": 0,
                                            "task_data": {
                                                "text": "Welcome to our platform! This narration greets every new listener.",
                                                "total_length": 420
                                            },
                                            "result": null,
                                            "created_at": "2026-07-17T16:00:00+00:00",
                                            "updated_at": "2026-07-17T16:00:00+00:00",
                                            "eta_seconds": null,
                                            "eta_at": null,
                                            "completed_chunks": 0,
                                            "total_chunks": 2,
                                            "progress_percent": 0
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Невозможно выполнить retry: reason=invalid_order_status (исходный заказ не completed/result_delivered), has_failed_items (в заказе есть упавшие фрагменты) или retry_config_outdated (настройки исходной генерации — снимок шаблона и config_override — не проходят текущую схему сервиса; создайте новый заказ)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This order contains items that failed and cannot be repeated.",
                                        "reason": "has_failed_items",
                                        "error_code": "has_failed_items"
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу или у ключа нет права orders.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Не хватает денег на новый заказ сверх квоты подписки (`reason: insufficient_balance`) — биллинг тот же, что при создании. Только в режиме отката: `payg_topup_required` — получите `quote_token` и повторите запрос с `confirm_payg_topup: true`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "enum": [
                                                "insufficient_balance",
                                                "insufficient_allowance",
                                                "payg_topup_required"
                                            ],
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "enum": [
                                                "insufficient_balance",
                                                "insufficient_allowance",
                                                "payg_topup_required"
                                            ],
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "shortfall_tokens": {
                                            "type": "integer",
                                            "example": 1200,
                                            "description": "Сколько токенов не покрыла квота подписки — именно они оплачиваются из LMC."
                                        },
                                        "shortfall_lmc": {
                                            "type": "number",
                                            "example": 3.6,
                                            "description": "Стоимость доплаты в LMC, готовая к показу пользователю."
                                        },
                                        "shortfall_lmc_minor": {
                                            "type": "integer",
                                            "example": 360000000,
                                            "description": "Та же сумма в минимальных единицах LMC — для точных расчётов без потери копеек."
                                        },
                                        "quote_token": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "example": "q_9f8b2c1a4d3e",
                                            "description": "Подписанная котировка цены: верните её без изменений в повторном запросе вместе с `confirm_payg_topup: true`."
                                        },
                                        "expires_at": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "format": "date-time",
                                            "example": "2026-07-25T12:34:56+00:00",
                                            "description": "До этого момента цена зафиксирована. Позже — повторите запрос и получите новую котировку."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Insufficient balance to cover the part of the order beyond the subscription quota.",
                                        "reason": "insufficient_balance",
                                        "error_code": "insufficient_balance"
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Только в режиме отката к явному подтверждению: котировка добора устарела (reason=quote_mismatch) — запросите 402 заново",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "enum": [
                                                "quote_mismatch"
                                            ],
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "enum": [
                                                "quote_mismatch"
                                            ],
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "actual_lmc_minor": {
                                            "type": "integer",
                                            "example": 380000000,
                                            "description": "Во сколько добор обходится сейчас (минимальные единицы LMC)."
                                        },
                                        "quoted_lmc_minor": {
                                            "type": "integer",
                                            "example": 360000000,
                                            "description": "Цена, на которую вы согласились ранее (минимальные единицы LMC)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code",
                                        "actual_lmc_minor",
                                        "quoted_lmc_minor"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The pay-as-you-go quote is stale — request a fresh one.",
                                        "reason": "quote_mismatch",
                                        "error_code": "quote_mismatch",
                                        "actual_lmc_minor": 380000000,
                                        "quoted_lmc_minor": 360000000
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышена токен-квота ключа либо reason=limit_exceeded — исчерпан usage-лимит подписки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/items": {
            "get": {
                "operationId": "orderItem.index",
                "description": "Возвращает список всех элементов (чанков) заказа.\n\n**Информация о каждом элементе:**\n- Тип (`chunk`, `retry`, `regeneration`, `stitch`, `pause_edit` — legacy,\n  для новых заказов не создаётся)\n- Статус обработки (`pending`, `processing`, `completed`, `policy_flagged`, `failed`)\n- Текст и результат (`result_file` — основной файл чанка)\n- Стоимость и биллинг\n- Флаги `can_retry` / `can_regenerate` (отражают только статус чанка — см.\n  описание retry/regenerate ниже)",
                "summary": "List Order Items",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Список элементов заказа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/OrderItem"
                                            },
                                            "description": "Все фрагменты заказа, включая повторы и перегенерации. Порядок склейки задаёт `chunk_index`, а не позиция в списке."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order items retrieved successfully.",
                                        "data": [
                                            {
                                                "id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
                                                "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                                "item_type": "chunk",
                                                "chunk_index": 0,
                                                "parent_item_id": null,
                                                "original_text_length": 240,
                                                "current_text_length": 240,
                                                "price_units": 240,
                                                "consumed_units": 240,
                                                "remaining_locked": 0,
                                                "status": "completed",
                                                "result_file": {
                                                    "path": "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/chunks/0/result.mp3",
                                                    "storage": "s3",
                                                    "name": "chunk_0.mp3",
                                                    "size": 512340,
                                                    "mime_type": "audio/mpeg",
                                                    "size_formatted": "500.33 KB"
                                                },
                                                "attempt_number": 1,
                                                "can_retry": false,
                                                "can_regenerate": true,
                                                "created_at": "2026-07-17T12:00:05+00:00",
                                                "updated_at": "2026-07-17T12:02:10+00:00"
                                            },
                                            {
                                                "id": "e5f6a7b8-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                                                "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                                "item_type": "chunk",
                                                "chunk_index": 1,
                                                "parent_item_id": null,
                                                "original_text_length": 180,
                                                "current_text_length": 180,
                                                "price_units": 180,
                                                "consumed_units": 180,
                                                "remaining_locked": 0,
                                                "status": "completed",
                                                "result_file": {
                                                    "path": "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/chunks/1/result.mp3",
                                                    "storage": "s3",
                                                    "name": "chunk_1.mp3",
                                                    "size": 398210,
                                                    "mime_type": "audio/mpeg",
                                                    "size_formatted": "388.88 KB"
                                                },
                                                "attempt_number": 1,
                                                "can_retry": false,
                                                "can_regenerate": true,
                                                "created_at": "2026-07-17T12:00:05+00:00",
                                                "updated_at": "2026-07-17T12:02:45+00:00"
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Заказ не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу или у ключа нет права orders.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Не авторизован (отсутствует/невалидный X-API-KEY)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/items/{item}": {
            "get": {
                "operationId": "orderItem.show",
                "description": "Возвращает детальную информацию об элементе заказа.\n\n**Включает:**\n- Тип и статус элемента\n- Оригинальный и текущий текст (длины `original_text_length` / `current_text_length`)\n- Результат (`result_file`) при наличии\n- Флаги `can_retry` / `can_regenerate`\n- `failure_reason` — причина сбоя человеко-читаемым текстом (при `failed`/`policy_flagged`)\n\n> **`failure_reason` — локализованный текст, а не машинный код.** Строка переводится\n> по локали запроса и предназначена для показа человеку; ветвить на ней логику нельзя —\n> для этого есть `status`. Сырые детали ошибки (`metadata`, `policy_reason`) остаются\n> только в админ-контексте. У остальных статусов поле равно `null`; `null` возможен и у\n> упавшего элемента, если причина не распознана. См. плейбук обработки сбоев в\n> `POST .../retry` ниже.",
                "summary": "Get Order Item",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    },
                    {
                        "name": "item",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID фрагмента заказа (чанка) — 36 символов, не число и не порядковый номер.\n\nБерётся из поля `id` элемента: `GET /orders/{order}/items` отдаёт список, тот же\nнабор лежит в `items[]` детального ответа заказа. Порядковый номер фрагмента —\nэто соседнее поле `chunk_index`, в путь оно НЕ подставляется.",
                        "example": "2b7d1e60-5a44-4a1e-9f3c-7c0d9e8a1b23"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Информация об элементе",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/OrderItem",
                                            "description": "Один фрагмент заказа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order item loaded successfully.",
                                        "data": {
                                            "id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
                                            "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                            "item_type": "chunk",
                                            "chunk_index": 0,
                                            "parent_item_id": null,
                                            "original_text_length": 240,
                                            "current_text_length": 240,
                                            "price_units": 240,
                                            "consumed_units": 240,
                                            "remaining_locked": 0,
                                            "status": "completed",
                                            "result_file": {
                                                "path": "storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/chunks/0/result.mp3",
                                                "storage": "s3",
                                                "name": "chunk_0.mp3",
                                                "size": 512340,
                                                "mime_type": "audio/mpeg",
                                                "size_formatted": "500.33 KB"
                                            },
                                            "attempt_number": 1,
                                            "can_retry": false,
                                            "can_regenerate": true,
                                            "created_at": "2026-07-17T12:00:05+00:00",
                                            "updated_at": "2026-07-17T12:02:10+00:00"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Элемент не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу или у ключа нет права orders.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Не авторизован (отсутствует/невалидный X-API-KEY)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/items/{item}/text": {
            "get": {
                "operationId": "orderItem.text",
                "description": "Возвращает текст, с которым чанк ушёл в генерацию.\n\nОсновной сценарий — починка чанка со статусом `policy_flagged`: провайдер отклонил\nсодержимое, и повтор с тем же текстом будет отклонён снова. Порядок такой:\nзабрать текст этой ручкой → переформулировать спорный фрагмент → отправить\n`POST /orders/{order}/items/{item}/retry` с полем `text`.\n\nПричину отклонения API не сообщает — поля с ней нет; правьте текст по своему\nусмотрению.\n\nЕсли текст удалён по сроку хранения заказа — `422` с `reason: input_expired`.\nВосстановить его нельзя: создайте заказ заново (повтор с новым `text` при\nэтом по-прежнему доступен).\n\n**Требуемое право ключа:** `orders.read`.",
                "summary": "Get Order Item Text",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    },
                    {
                        "name": "item",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID фрагмента заказа (чанка) — 36 символов, не число и не порядковый номер.\n\nБерётся из поля `id` элемента: `GET /orders/{order}/items` отдаёт список, тот же\nнабор лежит в `items[]` детального ответа заказа. Порядковый номер фрагмента —\nэто соседнее поле `chunk_index`, в путь оно НЕ подставляется.",
                        "example": "2b7d1e60-5a44-4a1e-9f3c-7c0d9e8a1b23"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Текст элемента",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "text": {
                                                    "type": "string",
                                                    "description": "Текст фрагмента как есть — вместе со всеми тегами. Именно его отправляют в `regenerate`, поправив нужное."
                                                },
                                                "length": {
                                                    "type": "integer",
                                                    "description": "Полная длина текста в символах, вместе с тегами."
                                                },
                                                "original_text_length": {
                                                    "type": "integer",
                                                    "description": "Синоним `free_length`, оставлен для обратной совместимости."
                                                },
                                                "free_length": {
                                                    "type": "integer",
                                                    "description": "Наибольшая уже оплаченная длина фрагмента: в её пределах повтор бесплатен, сверх — доплата."
                                                },
                                                "billable_length": {
                                                    "type": "integer",
                                                    "description": "Сколько символов текущего текста оплачивается. Бесплатны только наши теги пауз `{{pause=2s}}`; `[шёпот]` и `<break/>` уходят провайдеру и тарифицируются."
                                                },
                                                "max_raw_length": {
                                                    "type": "integer",
                                                    "description": "Технический предел длины фрагмента — сколько система отдаст воркеру одним куском, в единице `capacity_unit`. Превышение — `422`."
                                                },
                                                "max_billable_length": {
                                                    "type": "integer",
                                                    "description": "Предел по оплачиваемым символам. Превышение делает перегенерацию дороже либо недоступной."
                                                },
                                                "surcharge_applies": {
                                                    "type": "string",
                                                    "description": "`true` — за длину сверх нормы будет доплата. Проверьте до отправки `regenerate`, чтобы не удивиться списанию."
                                                },
                                                "capacity_unit": {
                                                    "type": "string",
                                                    "description": "Единица `max_raw_length` и `raw_length`: `chars` — символы, `utf8_bytes` — байты UTF-8 (Fish Audio считает лимит запроса в байтах: русская буква — 2 байта, латинская — 1)."
                                                },
                                                "raw_length": {
                                                    "type": "integer",
                                                    "description": "Длина текущего текста в `capacity_unit` — то, что сравнивается с `max_raw_length`."
                                                }
                                            },
                                            "required": [
                                                "text",
                                                "length",
                                                "original_text_length",
                                                "free_length",
                                                "billable_length",
                                                "max_raw_length",
                                                "max_billable_length",
                                                "surcharge_applies",
                                                "capacity_unit",
                                                "raw_length"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "text": "Welcome to our platform! This narration greets every new listener.",
                                            "length": 66,
                                            "original_text_length": 240
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Элемент не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу или у ключа нет права orders.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Не авторизован (отсутствует/невалидный X-API-KEY)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Текст чанка удалён по сроку хранения (`reason: input_expired`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "reason": "string",
                                        "error_code": "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/items/retry-failed": {
            "post": {
                "operationId": "orderItem.retryFailed",
                "description": "Ставит на повтор все упавшие чанки заказа одним вызовом — вместо обхода\n`POST .../items/{item}/retry` по каждому. Тело запроса пустое.\n\nБерутся только чанки в статусе `failed` (техническая ошибка), у которых нет\nболее поздней успешной версии. `policy_flagged` сюда НЕ входит: повтор с тем\nже текстом будет отклонён снова, такие чанки перезапускают поштучно с\nисправленным `text`.\n\n**Ответ `202` — план принят, работа идёт в фоне.** Тело содержит `queued`\n(поставленные на повтор) и `skipped` с машинной причиной на каждый пропуск:\n`superseded` (уже есть успешная версия), `active_retry_exists`,\n`policy_flagged`, `max_attempts_reached`, `insufficient_funds`.\nОпрашивайте `GET .../items` либо слушайте WebSocket — синхронного результата\nу этого вызова нет по построению.\n\n**Биллинг:** переиспользует заблокированный остаток каждого чанка — новых\nсредств и токен-квоты ключа не расходует.\n\n**Отказы на уровне заказа** приходят телом `{success: false, message, reason}`,\nгде `reason` — машинный код: `409` `stitch_in_progress` (по заказу идёт\nсклейка); `422` `invalid_order_status` (заказ не в статусе\n`partially_completed`), `generative_not_supported` (тип задачи не\nподдерживает повтор), `nothing_to_retry` (перезапускать нечего) либо\n`retry_config_outdated` (настройки заказа не проходят текущую схему сервиса —\nповтор не ставится, нужен новый заказ); `503` `service_maintenance` (приём\nсервиса, исполняющего заказ, приостановлен). Эти отказы синхронны: план не\nпринимается, `bulk_retry.completed` не приходит.\nОтдельными схемами в спеке они не описаны: коды рождаются в\nбизнес-исключении, а не в валидации запроса.\n\n**Требуемое право ключа:** `orders.write`. Недоступно для `sfx`/`music`.",
                "summary": "Retry Failed Order Items",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "responses": {
                    "202": {
                        "description": "План принят, перезапуск сбойных чанков поставлен в очередь. Следите за статусом заказа.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "queued_count": {
                                                    "type": "integer",
                                                    "description": "Сколько фрагментов принято к перезапуску."
                                                },
                                                "queued": {
                                                    "type": "array",
                                                    "items": {},
                                                    "description": "Что именно поставлено в очередь. Отслеживать выполнение — по заказу целиком, отдельных событий на фрагмент нет."
                                                },
                                                "skipped_count": {
                                                    "type": "integer",
                                                    "description": "Сколько фрагментов перезапустить не удалось. Ненулевое значение — нормальный исход, а не ошибка: часть причин неустранима."
                                                },
                                                "skipped": {
                                                    "type": "array",
                                                    "items": {},
                                                    "description": "Пропущенные фрагменты с причиной по каждому."
                                                }
                                            },
                                            "required": [
                                                "queued_count",
                                                "queued",
                                                "skipped_count",
                                                "skipped"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Failed chunks queued for retry",
                                        "data": {
                                            "queued_count": 2,
                                            "queued": [
                                                {
                                                    "item_id": "f8a9b0c1-2d3e-4f4a-8b5c-6d7e8f9a0b1c",
                                                    "chunk_index": 2
                                                },
                                                {
                                                    "item_id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                                                    "chunk_index": 5
                                                }
                                            ],
                                            "skipped_count": 1,
                                            "skipped": [
                                                {
                                                    "item_id": "d2e3f4a5-6b7c-4d8e-8f9a-1b2c3d4e5f6a",
                                                    "chunk_index": 7,
                                                    "reason": "max_attempts_reached",
                                                    "reason_label": "The chunk has run out of retry attempts."
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Заказ не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу или у ключа нет права orders.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Не авторизован (отсутствует/невалидный X-API-KEY)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/items/{item}/retry": {
            "post": {
                "operationId": "orderItem.retry",
                "description": "Повторно отправляет на обработку сбойный чанк заказа. Применимо к чанку в статусе\n`policy_flagged` **или** `failed`, когда сам заказ — `partially_completed`.\nТак «дотягивают» частично выполненный заказ до `completed` — поштучно, по каждому\nсбойному чанку (order-level `POST /orders/{order}/retry` для этого НЕ подходит: он\nсоздаёт новый заказ и требует `completed`/`result_delivered`).\n\n**Биллинг:** в пределах уже оплаченной длины (`free_length`) использует\nзаблокированный остаток оригинального чанка — новых средств и токен-квоты\nключа НЕ расходует. Текст ДЛИННЕЕ оплаченного тарифицируется доплатой за\nпревышение: она списывается quota-first, учитывается в токен-квоте ключа\n(только дельта) и возвращается, если повтор не удался.\n\n**Поле `text` (опциональное):** заменяет текст чанка. Длину и оба предела\nотдаёт `GET .../items/{item}/text`: `free_length` — бесплатный объём,\n`max_billable_length` — предел с доплатой, `max_raw_length` — ёмкость\nфрагмента (её оплатой не снять) в единице `capacity_unit` (`chars` или\n`utf8_bytes` — у Fish Audio). Бесплатны и места не занимают только наши\nпаузы `{{pause=2s}}`; `<break .../>` и указания диктору в `[…]` (`[шёпот]`) —\nсинтаксис провайдера: они тарифицируются и входят в лимит (llm.md §8.3).\nЕсли не передать — повторяется **тот же** текст; если он уже удалён по сроку\nхранения заказа — `422` с `reason: input_expired` (с новым `text` повтор проходит).\n\n> **policy_flagged:** контент отклонён политикой провайдера (напр. ElevenLabs).\n> Повтор с тем же текстом будет отклонён снова — передайте `text` с исправленной\n> формулировкой. Конкретную причину API не сообщает (см. `GET .../items/{item}`),\n> правьте по своему усмотрению (уберите/перефразируйте потенциально проблемный фрагмент).\n> **failed:** техническая ошибка — обычно достаточно повторить (можно с укороченным\n> текстом). Лимит попыток чанка — `ORDERS_MAX_RETRY_ATTEMPTS` (по умолчанию 5).\n\n**Требуемое право ключа:** `orders.write`. Недоступно для `sfx`/`music`.",
                "summary": "Retry Order Item",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    },
                    {
                        "name": "item",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID фрагмента заказа (чанка) — 36 символов, не число и не порядковый номер.\n\nБерётся из поля `id` элемента: `GET /orders/{order}/items` отдаёт список, тот же\nнабор лежит в `items[]` детального ответа заказа. Порядковый номер фрагмента —\nэто соседнее поле `chunk_index`, в путь оно НЕ подставляется.",
                        "example": "2b7d1e60-5a44-4a1e-9f3c-7c0d9e8a1b23"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/RetryOrderItemRequest",
                                "example": {
                                    "text": "string"
                                }
                            },
                            "examples": {
                                "same_text": {
                                    "summary": "Повтор как есть",
                                    "description": "Для чанка в статусе failed (техническая ошибка) — обычно достаточно повторить прежний текст.",
                                    "value": {}
                                },
                                "fixed_text": {
                                    "summary": "С исправленным текстом",
                                    "description": "Для policy_flagged: повтор с тем же текстом будет отклонён снова — присылайте переформулированный.",
                                    "value": {
                                        "text": "Переформулированный фрагмент текста."
                                    }
                                }
                            }
                        }
                    },
                    "description": "Тело не обязательно: без него фрагмент повторяется с прежним текстом. Поле `text`\nнужно после отказа контентной политики (`policy_flagged`) — тот же текст будет\nотклонён снова.\n\nВ пределах оплаченной длины (`free_length` из `GET …/items/{item}/text`) повтор\nбесплатен — цену наследует исходный фрагмент. Текст длиннее оплаченного (до\n`max_billable_length`) тарифицируется доплатой за превышение; не хватает денег — `402`."
                },
                "responses": {
                    "201": {
                        "description": "Чанк отправлен на повтор. Заказ остаётся в `partially_completed`, пока не завершатся все чанки.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/OrderItem",
                                            "description": "Новая запись повтора со своим `id`; `parent_item_id` указывает на исходный фрагмент. Повтор бесплатен — новых токенов он не стоит."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order item retry created successfully.",
                                        "data": {
                                            "id": "a9b0c1d2-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
                                            "order_id": "e7f8a9b0-1c2d-4e3f-8a4b-5c6d7e8f9a0b",
                                            "item_type": "retry",
                                            "chunk_index": 2,
                                            "parent_item_id": "f8a9b0c1-2d3e-4f4a-8b5c-6d7e8f9a0b1c",
                                            "original_text_length": 96,
                                            "current_text_length": 96,
                                            "price_units": 96,
                                            "consumed_units": 0,
                                            "remaining_locked": 96,
                                            "status": "pending",
                                            "result_file": null,
                                            "attempt_number": 2,
                                            "can_retry": false,
                                            "can_regenerate": false,
                                            "created_at": "2026-07-17T13:40:00+00:00",
                                            "updated_at": "2026-07-17T13:40:00+00:00"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Прочий отказ без машинного кода (редко): причина — только в `message`. Отказы с `reason` приходят кодами 402/409/422/503.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The operation could not be completed."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Заказ или элемент не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу, у ключа нет права orders.write, либо нет подписки на сервис (`reason: no_subscription`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Повтор невозможен (`reason`): `invalid_order_status` — заказ не `partially_completed`; `invalid_status` — чанк не `failed`/`policy_flagged`; `max_attempts_reached` — исчерпан лимит попыток (5); `generative_not_supported` — `sfx`/`music`; `text_over_capacity`, `content_blocked`, `input_expired` — см. описание; `retry_config_outdated` — настройки заказа (снимок шаблона с `config_override`) не проходят текущую схему сервиса, повторить фрагмент «как был» нельзя — создайте новый заказ. Либо ошибка валидации тела (`errors`).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Не авторизован (отсутствует/невалидный X-API-KEY)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Не хватает средств: `reason: insufficient_funds` — у чанка не осталось залоченного остатка; `insufficient_balance`/`insufficient_allowance` — не на что оплатить доплату за текст длиннее оплаченного.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Insufficient balance.",
                                        "reason": "insufficient_balance",
                                        "error_code": "insufficient_balance"
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Повтор этого чанка уже идёт (`reason: active_retry_exists`) либо заказ склеивается (`reason: stitch_in_progress`). Повторите позже.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "A retry for this item is already in progress.",
                                        "reason": "active_retry_exists",
                                        "error_code": "active_retry_exists"
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышена токен-квота ключа (доплата за текст длиннее оплаченного)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Приём работы на сервис временно приостановлен (`reason: service_maintenance`). Повторите позже.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The service is temporarily unavailable due to maintenance. Please try again later.",
                                        "reason": "service_maintenance",
                                        "error_code": "service_maintenance"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/orders/{order}/items/{item}/regenerate": {
            "post": {
                "operationId": "orderItem.regenerate",
                "description": "Перегенерирует завершённый (`completed`) элемент заказа с новым текстом.\n\n**Биллинг:** платная операция — блокирует НОВЫЕ средства. Стоимость учитывается\nв токен-квоте ключа (pre-check до загрузки текста → `429` при превышении,\nперегенерация не выполняется; запись фактического объёма — после успеха).\n\nПоле `text` обязательно. Предел длины — ёмкость фрагмента (`max_billable_length`\nиз `GET .../items/{item}/text`); длиннее — `422` с `reason: text_over_capacity`.\nСтоимость считается по всей новой длине. Бесплатны и места не занимают только\nнаши паузы `{{pause=2s}}`; `<break .../>` и указания диктору в `[…]` (`[шёпот]`) —\nсинтаксис провайдера: они тарифицируются и входят в лимит (llm.md §8.3).\nВ отличие от retry, regenerate применим только к **уже завершённому** (`completed`)\nчанку и стоит новых токенов.\n\n**Требуемое право ключа:** `orders.write`. Недоступно для `sfx`/`music`.",
                "summary": "Regenerate Order Item",
                "tags": [
                    "Public Orders"
                ],
                "parameters": [
                    {
                        "name": "order",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID заказа — 36 символов вида `9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c`, не число.\n\nБерётся из поля `id`: его возвращает `POST /orders` при создании и `GET /orders`\nв списке. Чужой заказ по вашему ключу не откроется — придёт `404`.",
                        "example": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                    },
                    {
                        "name": "item",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID фрагмента заказа (чанка) — 36 символов, не число и не порядковый номер.\n\nБерётся из поля `id` элемента: `GET /orders/{order}/items` отдаёт список, тот же\nнабор лежит в `items[]` детального ответа заказа. Порядковый номер фрагмента —\nэто соседнее поле `chunk_index`, в путь оно НЕ подставляется.",
                        "example": "2b7d1e60-5a44-4a1e-9f3c-7c0d9e8a1b23"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/RegenerateOrderItemRequest",
                                "example": {
                                    "text": "string"
                                }
                            },
                            "examples": {
                                "new_text": {
                                    "summary": "Новый текст фрагмента",
                                    "description": "Переделка уже готового чанка. Тарифицируется как новая генерация.",
                                    "value": {
                                        "text": "Новая редакция фрагмента."
                                    }
                                }
                            }
                        }
                    },
                    "description": "Переделка уже завершённого фрагмента новым текстом. Операция **платная** — токены\nсписываются как за новую генерацию.\n\n`text` обязателен. Предел длины — ёмкость фрагмента (`max_billable_length` из\n`GET …/items/{item}/text`); длиннее — `422` с `reason: text_over_capacity`. Стоимость\nсчитается по всей новой длине. Бесплатны только наши паузы `{{pause=2s}}`: `<break/>`\nи любые указания диктору в квадратных скобках (`[шёпот]`, но и `[1]`, `[sic]`) —\nсинтаксис провайдера, они тарифицируются и занимают место во фрагменте."
                },
                "responses": {
                    "201": {
                        "description": "Чанк отправлен на переделку. Операция платная: списывается новая токен-квота.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/OrderItem",
                                            "description": "Новая запись перегенерации со своим `id`. Операция платная: токены списываются заново, прежняя версия фрагмента остаётся в истории."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Order item regeneration created successfully.",
                                        "data": {
                                            "id": "b0c1d2e3-4f5a-4b6c-8d7e-8f9a0b1c2d3e",
                                            "order_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                            "item_type": "regeneration",
                                            "chunk_index": 0,
                                            "parent_item_id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
                                            "original_text_length": 210,
                                            "current_text_length": 210,
                                            "price_units": 210,
                                            "consumed_units": 0,
                                            "remaining_locked": 210,
                                            "status": "pending",
                                            "result_file": null,
                                            "attempt_number": 1,
                                            "can_retry": false,
                                            "can_regenerate": false,
                                            "created_at": "2026-07-17T14:10:00+00:00",
                                            "updated_at": "2026-07-17T14:10:00+00:00"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Прочий отказ без машинного кода (редко): причина — только в `message`. Отказы с `reason` приходят кодами 402/422/503.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The operation could not be completed."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Не передан обязательный `text` либо переделка невозможна (`reason`): `invalid_status` — чанк не `completed`, `text_over_capacity`, `text_not_voiceable`, `content_blocked`, `generative_not_supported` (`sfx`/`music`), `retry_config_outdated` — настройки заказа не проходят текущую схему сервиса, создайте новый заказ.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "errors": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Заказ или элемент не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к заказу, у ключа нет права orders.write, либо нет подписки на сервис (`reason: no_subscription`)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Не авторизован (отсутствует/невалидный X-API-KEY)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Не хватает средств на перегенерацию (`reason: insufficient_balance`/`insufficient_allowance`).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Insufficient balance.",
                                        "reason": "insufficient_balance",
                                        "error_code": "insufficient_balance"
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Превышена токен-квота ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "retry_after": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ],
                                            "description": "Через сколько секунд имеет смысл повторить запрос (то же значение — в заголовке `Retry-After`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Too many attempts. Please wait a moment and try again.",
                                        "reason": "rate_limit_exceeded",
                                        "error_code": "rate_limit_exceeded",
                                        "retry_after": 30
                                    }
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Приём работы на сервис временно приостановлен (`reason: service_maintenance`). Повторите позже.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина бизнес-отказа (например, `input_expired`, `insufficient_balance`). Ветвите обработку по ней, а не по тексту `message`."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The service is temporarily unavailable due to maintenance. Please try again later.",
                                        "reason": "service_maintenance",
                                        "error_code": "service_maintenance"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/user": {
            "get": {
                "operationId": "profile.show",
                "description": "Возвращает профиль пользователя, которому принадлежит API-ключ.\n\nБазовый ответ: идентификатор, email, язык интерфейса (`lang_code`), признак привязки\nTelegram (`has_telegram`) и даты создания/обновления.\n\nВсё остальное подключается параметром `include` — список секций через запятую. Полный\nперечень допустимых значений с расшифровкой смотрите в описании самого параметра\n(вкладка **Params**); неизвестное значение молча игнорируется, ошибки не будет.\n\nЧастый сценарий — узнать, сколько генерации осталось:\n`GET /user?include=subscriptions,usage`.\n\nЭндпоинт read-only и всегда отдаёт данные строго в контексте владельца ключа:\nслужебные и административные поля не возвращаются.\n\n**Требуемое право ключа:** `profile.read`.",
                "summary": "Get profile of API key owner",
                "tags": [
                    "Public Profile"
                ],
                "parameters": [
                    {
                        "name": "include",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "maxLength": 200,
                            "x-enum-values": [
                                "wallets",
                                "telegram",
                                "subscriptions",
                                "referral",
                                "order_settings",
                                "entitlements",
                                "usage",
                                "unread_notifications_count"
                            ]
                        },
                        "description": "Какие дополнительные секции подмешать в ответ. Список через запятую, **без пробелов**.\nНеизвестное значение молча игнорируется (ошибки не будет), поэтому опечатка выглядит\nкак «секция не пришла».\n\n| Значение | Что добавляет в ответ |\n| --- | --- |\n| `subscriptions` | Активные подписки с остатком токенов (`subscriptions[].items[]`) — основной способ узнать, сколько генерации осталось |\n| `usage` | Текущее использование лимитов по сервисам (то же, что `GET /usage`) |\n| `entitlements` | Возможности, выданные тарифом: какие сервисы вам доступны |\n| `wallets` | Кошельки и балансы LMC. В режиме «без аванса» секция не возвращается даже при явном запросе — остаток услуги смотрите в `subscriptions` |\n| `order_settings` | Пользовательские настройки заказа (отложенный старт и его задержка) |\n| `telegram` | Привязанный Telegram-аккаунт |\n| `referral` | Активная реферальная программа. Не возвращается, если реферальная механика отключена |\n| `unread_notifications_count` | Число непрочитанных уведомлений (целое число в корне `data`) |\n\nПример: `?include=subscriptions,usage,entitlements`.",
                        "example": "subscriptions,usage"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Профиль владельца ключа с опциональными includes",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "id": {
                                                    "type": "integer",
                                                    "description": "ID аккаунта — владельца ключа."
                                                },
                                                "email": {
                                                    "type": "string",
                                                    "description": "Почта аккаунта."
                                                },
                                                "lang_code": {
                                                    "type": "string",
                                                    "description": "Язык аккаунта, ISO-639-1. На нём приходят `message` и переводимые названия."
                                                },
                                                "has_telegram": {
                                                    "type": "boolean",
                                                    "description": "Привязан ли Telegram. Подробности привязки — через `?include=telegram`."
                                                },
                                                "created_at": {
                                                    "type": "string",
                                                    "description": "Когда аккаунт зарегистрирован, ISO 8601."
                                                },
                                                "updated_at": {
                                                    "type": "string",
                                                    "description": "Когда профиль последний раз менялся, ISO 8601."
                                                }
                                            },
                                            "required": [
                                                "id",
                                                "email",
                                                "lang_code",
                                                "has_telegram",
                                                "created_at",
                                                "updated_at"
                                            ],
                                            "description": "Профиль владельца ключа. Шесть полей ниже приходят всегда; всё остальное\nдобавляется параметром `include` и в схеме не описано — форма секции\nсовпадает с ответом профильного эндпоинта:\n\n| Значение `include` | Что добавится |\n| --- | --- |\n| `subscriptions` | То же, что отдаёт `GET /subscriptions` |\n| `usage` | То же, что отдаёт `GET /usage` |\n| `entitlements` | Возможности активного тарифа |\n| `wallets` | Кошельки и остатки LMC |\n| `order_settings` | Сохранённые предпочтения создания заказа |\n| `telegram` | Привязка Telegram |\n| `referral` | Реферальный код и статистика |\n| `unread_notifications_count` | Число непрочитанных уведомлений |"
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": 501,
                                            "email": "ava.creator@example.com",
                                            "lang_code": "en",
                                            "display_currency": null,
                                            "has_telegram": false,
                                            "created_at": "2026-01-15T09:00:00+00:00",
                                            "updated_at": "2026-07-15T09:05:00+00:00",
                                            "subscriptions": [
                                                {
                                                    "id": 301,
                                                    "user_id": 501,
                                                    "status": "active",
                                                    "billing_anchor_at": "2026-01-15T00:00:00+00:00",
                                                    "current_period_start": "2026-07-15T00:00:00+00:00",
                                                    "current_period_end": "2026-08-15T00:00:00+00:00",
                                                    "canceled_at": null,
                                                    "items": [
                                                        {
                                                            "id": 601,
                                                            "subscription_id": 301,
                                                            "subscription_plan_id": 3,
                                                            "plan": {
                                                                "id": 3,
                                                                "code": "creator",
                                                                "name": "Creator",
                                                                "description": "For solo creators publishing regularly across voice and image generation.",
                                                                "tagline": "Solo creators publishing weekly across voice and image.",
                                                                "price_monthly": 2200,
                                                                "price_monthly_formatted": 22,
                                                                "discount": 0,
                                                                "effective_price_monthly": 2200,
                                                                "effective_price_monthly_formatted": 22,
                                                                "asset": {
                                                                    "id": 1,
                                                                    "code": "LUMC",
                                                                    "kind": "money",
                                                                    "precision": 2,
                                                                    "name": "LumCoin",
                                                                    "deleted_at": null,
                                                                    "created_at": "2026-01-10T09:00:00+00:00",
                                                                    "updated_at": "2026-01-10T09:00:00+00:00"
                                                                },
                                                                "token_allowance_monthly": 30000,
                                                                "concurrent_jobs": 4,
                                                                "team_seats": 1,
                                                                "priority_api_queue": false,
                                                                "commercial_licence": true,
                                                                "is_popular": false,
                                                                "is_active": true,
                                                                "sort_order": 30,
                                                                "metadata": null,
                                                                "created_at": "2026-05-10T00:00:00+00:00",
                                                                "updated_at": "2026-05-10T00:00:00+00:00"
                                                            },
                                                            "status": "active",
                                                            "is_usable": true,
                                                            "billing_period": "monthly",
                                                            "quantity": 1,
                                                            "unit_amount": 2200,
                                                            "unit_amount_formatted": 22,
                                                            "currency_asset": {
                                                                "id": 1,
                                                                "code": "LUMC",
                                                                "kind": "money",
                                                                "precision": 2,
                                                                "name": "LumCoin",
                                                                "deleted_at": null,
                                                                "created_at": "2026-01-10T09:00:00+00:00",
                                                                "updated_at": "2026-01-10T09:00:00+00:00"
                                                            },
                                                            "token_allowance": 30000,
                                                            "tokens_used": 8200,
                                                            "tokens_remaining": 21800,
                                                            "unlimited_service_codes": [],
                                                            "start_at": "2026-07-15T00:00:00+00:00",
                                                            "end_at": "2026-08-15T00:00:00+00:00",
                                                            "canceled_at": null,
                                                            "created_at": "2026-07-15T09:05:00+00:00"
                                                        }
                                                    ],
                                                    "created_at": "2026-01-15T09:05:00+00:00",
                                                    "updated_at": "2026-07-15T09:05:00+00:00"
                                                }
                                            ],
                                            "entitlements": [
                                                {
                                                    "id": 850,
                                                    "user_id": 501,
                                                    "entitlement_id": 1,
                                                    "entitlement": {
                                                        "id": 1,
                                                        "code": "beta_access",
                                                        "name": "Beta Access",
                                                        "metadata": null,
                                                        "is_active": true,
                                                        "created_at": "2026-01-10T09:00:00+00:00",
                                                        "updated_at": "2026-01-10T09:00:00+00:00"
                                                    },
                                                    "source_subscription_item_id": 601,
                                                    "starts_at": "2026-07-15T00:00:00+00:00",
                                                    "ends_at": "2026-08-15T00:00:00+00:00",
                                                    "status": "active",
                                                    "created_at": "2026-07-15T09:05:00+00:00",
                                                    "is_unlimited": false
                                                },
                                                {
                                                    "id": 851,
                                                    "user_id": 501,
                                                    "entitlement_id": 2,
                                                    "entitlement": {
                                                        "id": 2,
                                                        "code": "elevenlabs_tts",
                                                        "name": "entitlement.name.elevenlabs_tts",
                                                        "metadata": null,
                                                        "is_active": true,
                                                        "created_at": "2026-01-10T09:00:00+00:00",
                                                        "updated_at": "2026-01-10T09:00:00+00:00"
                                                    },
                                                    "source_subscription_item_id": 601,
                                                    "starts_at": "2026-07-15T00:00:00+00:00",
                                                    "ends_at": "2026-08-15T00:00:00+00:00",
                                                    "status": "active",
                                                    "created_at": "2026-07-15T09:05:00+00:00",
                                                    "is_unlimited": false
                                                }
                                            ],
                                            "usage": [
                                                {
                                                    "limit_id": 10,
                                                    "service_id": 1,
                                                    "service_code": "sfx",
                                                    "service_name": "Sound Effects",
                                                    "entitlement_id": null,
                                                    "entitlement_code": null,
                                                    "limit_type": "hourly",
                                                    "limit_value": 100,
                                                    "used": 0,
                                                    "remaining": 100,
                                                    "period_start": null,
                                                    "period_end": null,
                                                    "resets_in": null,
                                                    "token_unlimited": false
                                                },
                                                {
                                                    "limit_id": 11,
                                                    "service_id": 2,
                                                    "service_code": "music",
                                                    "service_name": "Music Generation",
                                                    "entitlement_id": null,
                                                    "entitlement_code": null,
                                                    "limit_type": "hourly",
                                                    "limit_value": 100,
                                                    "used": 0,
                                                    "remaining": 100,
                                                    "period_start": null,
                                                    "period_end": null,
                                                    "resets_in": null,
                                                    "token_unlimited": false
                                                },
                                                {
                                                    "limit_id": 12,
                                                    "service_id": 3,
                                                    "service_code": "elevenlabs",
                                                    "service_name": "ElevenLabs TTS",
                                                    "entitlement_id": 2,
                                                    "entitlement_code": "elevenlabs_tts",
                                                    "limit_type": "monthly",
                                                    "limit_value": 500000,
                                                    "used": 128400,
                                                    "remaining": 371600,
                                                    "period_start": "2026-07-01T00:00:00+00:00",
                                                    "period_end": "2026-08-01T00:00:00+00:00",
                                                    "resets_in": "2 weeks from now",
                                                    "token_unlimited": false
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "API-ключ отсутствует, неизвестен, неактивен или истёк",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права profile.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/reseller": {
            "get": {
                "operationId": "reseller.show",
                "description": "Профиль реселлера: статус, личная скидка, лимиты, баланс (`available`, `held`, `debt`). **Право:** `reseller.read`.",
                "summary": "Reseller profile",
                "tags": [
                    "Public Reseller"
                ],
                "responses": {
                    "200": {
                        "description": "Профиль реселлера",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/ResellerProfile",
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "data"
                                    ],
                                    "example": {
                                        "data": {
                                            "id": "string",
                                            "status": "string",
                                            "discount_bps": "string",
                                            "blocked_reason": "string",
                                            "approved_at": "string",
                                            "limits": "string",
                                            "balance": {
                                                "key": 1
                                            },
                                            "currency_asset": {
                                                "code": "string",
                                                "precision": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "`not_reseller` — у владельца ключа нет профиля реселлера; `gift_codes_disabled` — фича выключена",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/reseller/settings": {
            "get": {
                "operationId": "reseller.settings",
                "description": "Готовые номиналы (LMC-десятичные строки), границы своего номинала и окно самоотмены кода реселлера (`reseller_cancel_window_minutes`). Лимит кодов за один выпуск — в профиле. **Право:** `reseller.read`.",
                "summary": "Gift code settings",
                "tags": [
                    "Public Reseller"
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "nominals": {
                                            "type": "array",
                                            "items": {
                                                "type": "string"
                                            },
                                            "description": "Готовые номиналы кодов в LMC — десятичные строки без хвостовых нулей (`\"100\"`, `\"2.5\"`). В `POST /reseller/codes` можно передать любое значение между `min_nominal` и `max_nominal`, не только из этого списка."
                                        },
                                        "min_nominal": {
                                            "type": "string",
                                            "description": "Наименьший допустимый номинал одного кода, LMC (десятичная строка)."
                                        },
                                        "max_nominal": {
                                            "type": "string",
                                            "description": "Наибольший допустимый номинал одного кода, LMC (десятичная строка)."
                                        },
                                        "max_codes_per_purchase": {
                                            "type": "integer",
                                            "description": "Сколько кодов обычный покупатель может взять за одну покупку. На выпуск реселлера не влияет: его потолок — `limits.max_codes_per_issue` в `GET /reseller`."
                                        },
                                        "max_purchase_rub": {
                                            "type": "integer",
                                            "description": "Потолок одной рублёвой покупки кодов обычным покупателем, в рублях."
                                        },
                                        "hold_hours": {
                                            "type": "integer",
                                            "description": "На сколько часов после рублёвой покупки код заморожен для активации (поле `activatable_at` кода)."
                                        },
                                        "reseller_cancel_window_minutes": {
                                            "type": "integer",
                                            "description": "Окно самоотмены кода реселлером, минуты с момента выпуска: пока оно открыто, `POST /reseller/codes/{uuid}/cancel` вернёт стоимость на баланс."
                                        },
                                        "rub_enabled": {
                                            "type": "boolean",
                                            "description": "Включены ли покупки кодов за рубли. На выпуск реселлером с баланса не влияет."
                                        },
                                        "currency_asset": {
                                            "type": "object",
                                            "properties": {
                                                "code": {
                                                    "type": "string",
                                                    "description": "Код актива, в котором выражены суммы."
                                                },
                                                "precision": {
                                                    "type": "integer",
                                                    "description": "Сколько знаков после запятой у LMC: `real = value / 10^precision`."
                                                }
                                            },
                                            "required": [
                                                "code",
                                                "precision"
                                            ],
                                            "description": "Валюта сумм ответа — LMC. По `precision` суммы в минимальных единицах переводятся в целые LMC."
                                        }
                                    },
                                    "required": [
                                        "nominals",
                                        "min_nominal",
                                        "max_nominal",
                                        "max_codes_per_purchase",
                                        "max_purchase_rub",
                                        "hold_hours",
                                        "reseller_cancel_window_minutes",
                                        "rub_enabled",
                                        "currency_asset"
                                    ],
                                    "example": {
                                        "nominals": [
                                            "string"
                                        ],
                                        "min_nominal": "string",
                                        "max_nominal": "string",
                                        "max_codes_per_purchase": 1,
                                        "max_purchase_rub": 1,
                                        "hold_hours": 1,
                                        "reseller_cancel_window_minutes": 1,
                                        "rub_enabled": true,
                                        "currency_asset": {
                                            "code": "string",
                                            "precision": 1
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/reseller/statistics": {
            "get": {
                "operationId": "reseller.statistics",
                "description": "Статистика за окно `from`–`to` (даты `Y-m-d` в зоне `X-Timezone`, без заголовка — UTC; по умолчанию последние 30 суток): деньги, коды, разрез по номиналам, ряд по дням. **Право:** `reseller.read`.",
                "summary": "Reseller statistics",
                "tags": [
                    "Public Reseller"
                ],
                "parameters": [
                    {
                        "name": "from",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        },
                        "description": "Первые сутки окна статистики — календарная дата `Y-m-d` в зоне `X-Timezone` (без заголовка — UTC). Не передана — 30 суток до `to`.",
                        "example": "2026-10-01"
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        },
                        "description": "Последние сутки окна — календарная дата `Y-m-d` в зоне `X-Timezone`, не раньше `from`; сутки входят в окно целиком. Не передана — сегодня.",
                        "example": "2026-10-12"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех. Полезная нагрузка — в поле `data`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "period": {
                                                    "type": "object",
                                                    "properties": {
                                                        "from": {
                                                            "type": "string",
                                                            "format": "date-time",
                                                            "description": "Начало окна — начало суток `from`."
                                                        },
                                                        "to": {
                                                            "type": "string",
                                                            "format": "date-time",
                                                            "description": "Конец окна — конец суток `to`."
                                                        }
                                                    },
                                                    "required": [
                                                        "from",
                                                        "to"
                                                    ],
                                                    "description": "Окно, за которое посчитана статистика: границы из `from`/`to` (по умолчанию — последние 30 суток) в зоне `X-Timezone`."
                                                },
                                                "money": {
                                                    "type": "object",
                                                    "properties": {
                                                        "top_ups_count": {
                                                            "type": "integer",
                                                            "description": "Сколько пополнений баланса подтверждено в окне."
                                                        },
                                                        "topped_up_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Сумма пополнений в окне, минимальные единицы LMC: рубли и крипта вместе."
                                                        },
                                                        "topped_up_rub_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Часть пополнений, оплаченная рублями, в минимальных единицах LMC."
                                                        },
                                                        "topped_up_crypto_lmc_minor": {
                                                            "type": "string",
                                                            "description": "Часть пополнений, оплаченная криптой, в минимальных единицах LMC."
                                                        },
                                                        "topped_up_kopecks": {
                                                            "type": "integer",
                                                            "description": "Рублёвые пополнения в копейках — ровно столько заплачено провайдеру."
                                                        },
                                                        "refunds_count": {
                                                            "type": "integer",
                                                            "description": "Сколько возвратов остатка рублёвого баланса на карту сделано в окне."
                                                        },
                                                        "refunded_kopecks": {
                                                            "type": "integer",
                                                            "description": "Сумма этих возвратов, в копейках."
                                                        },
                                                        "issued_nominal_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Сумма номиналов кодов, выпущенных в окне, в минимальных единицах LMC."
                                                        },
                                                        "issued_cost_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Во сколько эти коды обошлись реселлеру (со скидкой), в минимальных единицах LMC."
                                                        },
                                                        "discount_given_lmc_minor": {
                                                            "type": "string",
                                                            "description": "Скидка за окно: сумма номиналов минус стоимость выпущенных кодов, в минимальных единицах LMC."
                                                        },
                                                        "cancelled_cost_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Стоимость кодов, отменённых в окне и вернувшихся на баланс, в минимальных единицах LMC."
                                                        }
                                                    },
                                                    "required": [
                                                        "top_ups_count",
                                                        "topped_up_lmc_minor",
                                                        "topped_up_rub_lmc_minor",
                                                        "topped_up_crypto_lmc_minor",
                                                        "topped_up_kopecks",
                                                        "refunds_count",
                                                        "refunded_kopecks",
                                                        "issued_nominal_lmc_minor",
                                                        "issued_cost_lmc_minor",
                                                        "discount_given_lmc_minor",
                                                        "cancelled_cost_lmc_minor"
                                                    ],
                                                    "description": "Деньги за окно. Суммы LMC — в минимальных единицах (`real = value / 10^currency_asset.precision`), рубли — в копейках."
                                                },
                                                "codes": {
                                                    "type": "object",
                                                    "properties": {
                                                        "issued": {
                                                            "type": "integer",
                                                            "description": "Сколько кодов выпущено в окне."
                                                        },
                                                        "redeemed": {
                                                            "type": "integer",
                                                            "description": "Из выпущенных в окне — сколько уже активировано."
                                                        },
                                                        "active": {
                                                            "type": "integer",
                                                            "description": "Из выпущенных в окне — сколько ещё ждут активации."
                                                        },
                                                        "cancelled": {
                                                            "type": "integer",
                                                            "description": "Из выпущенных в окне — сколько отменено реселлером."
                                                        },
                                                        "annulled": {
                                                            "type": "integer",
                                                            "description": "Из выпущенных в окне — сколько аннулировано платформой без возврата стоимости."
                                                        },
                                                        "activation_rate": {
                                                            "type": [
                                                                "number",
                                                                "null"
                                                            ],
                                                            "description": "Доля активированных среди выпущенных в окне, от 0 до 1 (4 знака). `null`, если за окно ничего не выпущено."
                                                        },
                                                        "avg_seconds_to_activation": {
                                                            "type": [
                                                                "integer",
                                                                "null"
                                                            ],
                                                            "description": "Среднее время от выпуска до активации, в секундах. `null`, если активированных нет."
                                                        }
                                                    },
                                                    "required": [
                                                        "issued",
                                                        "redeemed",
                                                        "active",
                                                        "cancelled",
                                                        "annulled",
                                                        "activation_rate",
                                                        "avg_seconds_to_activation"
                                                    ],
                                                    "description": "Коды, ВЫПУЩЕННЫЕ в окне, и то, что с ними стало на момент запроса."
                                                },
                                                "by_nominal": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "nominal_lmc_minor": {
                                                                "type": "integer",
                                                                "description": "Номинал, минимальные единицы LMC."
                                                            },
                                                            "issued": {
                                                                "type": "integer",
                                                                "description": "Сколько кодов этого номинала выпущено в окне."
                                                            },
                                                            "redeemed": {
                                                                "type": "integer",
                                                                "description": "Из них активировано."
                                                            }
                                                        },
                                                        "required": [
                                                            "nominal_lmc_minor",
                                                            "issued",
                                                            "redeemed"
                                                        ]
                                                    },
                                                    "description": "Разрез выпуска по номиналам, по возрастанию номинала."
                                                },
                                                "daily": {
                                                    "type": "array",
                                                    "items": {},
                                                    "description": "Ряд по суткам в зоне `X-Timezone`, по возрастанию даты. Элемент: `date` (`Y-m-d`), `issued` (выпущено), `redeemed` (активировано), `topped_up_lmc_minor` (пополнения, минимальные единицы LMC). Сутки без событий в ряд не попадают."
                                                },
                                                "balance": {
                                                    "type": [
                                                        "object",
                                                        "null"
                                                    ],
                                                    "properties": {
                                                        "available_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Сколько лежит на счёте реселлера."
                                                        },
                                                        "held_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Сколько заморожено."
                                                        },
                                                        "debt_lmc_minor": {
                                                            "type": "integer",
                                                            "description": "Долг перед платформой."
                                                        },
                                                        "spendable_lmc_minor": {
                                                            "type": [
                                                                "object",
                                                                "null"
                                                            ],
                                                            "description": "Сколько можно потратить на выпуск: доступный остаток за вычетом долга, не меньше нуля."
                                                        },
                                                        "rub_refundable_lmc_minor": {
                                                            "type": [
                                                                "object",
                                                                "null"
                                                            ],
                                                            "description": "Какая часть баланса обеспечена рублями и может быть возвращена на карту (за вычетом долга)."
                                                        }
                                                    },
                                                    "required": [
                                                        "available_lmc_minor",
                                                        "held_lmc_minor",
                                                        "debt_lmc_minor",
                                                        "spendable_lmc_minor",
                                                        "rub_refundable_lmc_minor"
                                                    ],
                                                    "description": "Баланс на момент запроса — не за окно. Суммы в минимальных единицах LMC."
                                                },
                                                "currency_asset": {
                                                    "type": "object",
                                                    "properties": {
                                                        "code": {
                                                            "type": "string",
                                                            "const": "LUMC",
                                                            "description": "Код актива, в котором выражены суммы."
                                                        },
                                                        "precision": {
                                                            "type": "integer",
                                                            "description": "Сколько знаков после запятой у LMC: `real = value / 10^precision`."
                                                        }
                                                    },
                                                    "required": [
                                                        "code",
                                                        "precision"
                                                    ],
                                                    "description": "Валюта сумм ответа — LMC. По `precision` суммы в минимальных единицах переводятся в целые LMC."
                                                }
                                            },
                                            "required": [
                                                "period",
                                                "money",
                                                "codes",
                                                "by_nominal",
                                                "daily",
                                                "balance",
                                                "currency_asset"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "period": {
                                                "from": "2026-07-17T12:34:56+00:00",
                                                "to": "2026-07-17T12:34:56+00:00"
                                            },
                                            "money": {
                                                "top_ups_count": 1,
                                                "topped_up_lmc_minor": 1,
                                                "topped_up_rub_lmc_minor": 1,
                                                "topped_up_crypto_lmc_minor": "string",
                                                "topped_up_kopecks": 1,
                                                "refunds_count": 1,
                                                "refunded_kopecks": 1,
                                                "issued_nominal_lmc_minor": 1,
                                                "issued_cost_lmc_minor": 1,
                                                "discount_given_lmc_minor": "string",
                                                "cancelled_cost_lmc_minor": 1
                                            },
                                            "codes": {
                                                "issued": 1,
                                                "redeemed": 1,
                                                "active": 1,
                                                "cancelled": 1,
                                                "annulled": 1,
                                                "activation_rate": 1,
                                                "avg_seconds_to_activation": 1
                                            },
                                            "by_nominal": [
                                                {
                                                    "nominal_lmc_minor": 1,
                                                    "issued": 1,
                                                    "redeemed": 1
                                                }
                                            ],
                                            "daily": [
                                                "string"
                                            ],
                                            "balance": {
                                                "available_lmc_minor": 1,
                                                "held_lmc_minor": 1,
                                                "debt_lmc_minor": 1,
                                                "spendable_lmc_minor": [],
                                                "rub_refundable_lmc_minor": []
                                            },
                                            "currency_asset": {
                                                "code": "string",
                                                "precision": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    }
                }
            }
        },
        "/reseller/codes": {
            "get": {
                "operationId": "reseller.codes",
                "description": "Коды, выпущенные владельцем ключа, новые первыми, С ПОЛНЫМ значением `code`. Фильтры `status`, `source`; страничная пагинация (`page`, `per_page` до 100). **Право:** `reseller.read`.",
                "summary": "List my gift codes",
                "tags": [
                    "Public Reseller"
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100
                        },
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "example": 20
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "$ref": "#/components/schemas/GiftCodeStatus"
                        },
                        "description": "Вернуть только коды в этом статусе. Неизвестное значение → `422`.",
                        "example": "active"
                    },
                    {
                        "name": "source",
                        "in": "query",
                        "schema": {
                            "$ref": "#/components/schemas/GiftCodeSource"
                        },
                        "description": "Вернуть только коды этого источника: `reseller` — выпущенные вами со своего баланса, `purchase` — купленные вами как обычным покупателем. Неизвестное значение → `422`.",
                        "example": "reseller"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "`GiftCodeCollection`",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/GiftCodeList",
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "data"
                                    ],
                                    "example": {
                                        "data": {
                                            "items": [
                                                {
                                                    "uuid": "string",
                                                    "code_masked": "string",
                                                    "code": "string",
                                                    "nominal": "string",
                                                    "nominal_formatted": 1,
                                                    "cost": "string",
                                                    "cost_formatted": 1,
                                                    "discount_bps": 1,
                                                    "source": "string",
                                                    "funding": "string",
                                                    "status": "string",
                                                    "activatable_at": "string",
                                                    "redeemed_at": "string",
                                                    "cancelled_at": "string",
                                                    "created_at": "string",
                                                    "currency_asset": {
                                                        "code": "string",
                                                        "precision": 1
                                                    }
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 1,
                                            "total": 1
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    }
                }
            },
            "post": {
                "operationId": "reseller.issueCodes",
                "description": "Выпускает коды со своей скидкой с баланса реселлера. В ответе — партия с ПОЛНЫМИ кодами.\n\n**Тело:** `nominals` — номиналы в LMC (строки или числа), по одному на код.\n\n**Идемпотентность:** передайте заголовок `Idempotency-Key` (1–64 символа `A-Z a-z 0-9 _ . : -`),\nчтобы сетевой повтор не выпустил вторую партию. Повтор с тем же ключом и теми же номиналами\nвозвращает прежнюю партию — 200 и заголовок `Idempotent-Replayed: true`, баланс не списывается;\nтот же ключ с другими номиналами — 422 `idempotency_key_mismatch`.\n\n**Право:** `reseller.write`.",
                "summary": "Issue gift codes",
                "tags": [
                    "Public Reseller"
                ],
                "parameters": [
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "description": "Необязательный ключ идемпотентности выпуска: 1–64 символа `A-Z a-z 0-9 _ . : -`.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/IssueGiftCodesRequest",
                                "example": {
                                    "nominals": [
                                        1
                                    ]
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Партия выпущена",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/GiftCodeBatch",
                                            "description": "Выпущенная партия. При повторе по `Idempotency-Key` (код 200, заголовок `Idempotent-Replayed: true`) — та же партия, что в первом ответе; баланс второй раз не списан."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "uuid": "string",
                                            "source": "string",
                                            "codes_count": "string",
                                            "nominal_total": "string",
                                            "nominal_total_formatted": 1,
                                            "cost_total": "string",
                                            "cost_total_formatted": 1,
                                            "discount_bps": 1,
                                            "codes": [
                                                {
                                                    "uuid": "string",
                                                    "code_masked": "string",
                                                    "code": "string",
                                                    "nominal": "string",
                                                    "nominal_formatted": 1,
                                                    "cost": "string",
                                                    "cost_formatted": 1,
                                                    "discount_bps": 1,
                                                    "source": "string",
                                                    "funding": "string",
                                                    "status": "string",
                                                    "activatable_at": "string",
                                                    "redeemed_at": "string",
                                                    "cancelled_at": "string",
                                                    "created_at": "string",
                                                    "currency_asset": {
                                                        "code": "string",
                                                        "precision": 1
                                                    }
                                                }
                                            ],
                                            "created_at": "string",
                                            "currency_asset": {
                                                "code": "string",
                                                "precision": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "200": {
                        "description": "Повтор по `Idempotency-Key`: прежняя партия",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/GiftCodeBatch",
                                            "description": "Выпущенная партия. При повторе по `Idempotency-Key` (код 200, заголовок `Idempotent-Replayed: true`) — та же партия, что в первом ответе; баланс второй раз не списан."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "string",
                                        "data": {
                                            "uuid": "string",
                                            "source": "string",
                                            "codes_count": "string",
                                            "nominal_total": "string",
                                            "nominal_total_formatted": 1,
                                            "cost_total": "string",
                                            "cost_total_formatted": 1,
                                            "discount_bps": 1,
                                            "codes": [
                                                {
                                                    "uuid": "string",
                                                    "code_masked": "string",
                                                    "code": "string",
                                                    "nominal": "string",
                                                    "nominal_formatted": 1,
                                                    "cost": "string",
                                                    "cost_formatted": 1,
                                                    "discount_bps": 1,
                                                    "source": "string",
                                                    "funding": "string",
                                                    "status": "string",
                                                    "activatable_at": "string",
                                                    "redeemed_at": "string",
                                                    "cancelled_at": "string",
                                                    "created_at": "string",
                                                    "currency_asset": {
                                                        "code": "string",
                                                        "precision": 1
                                                    }
                                                }
                                            ],
                                            "created_at": "string",
                                            "currency_asset": {
                                                "code": "string",
                                                "precision": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка валидации; `gift_code_nominal_not_allowed`, `reseller_too_many_codes`, `idempotency_key_mismatch`",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "`reseller_inactive` — кабинет заблокирован; `reseller_insufficient_balance` — не хватает баланса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "enum": [
                                                "quote_mismatch"
                                            ],
                                            "description": "Машиночитаемая причина отказа — по ней ветвите обработку, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "enum": [
                                                "quote_mismatch"
                                            ],
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Значения всегда совпадают; ветвитесь по любому из них."
                                        },
                                        "actual_lmc_minor": {
                                            "type": "integer",
                                            "example": 380000000,
                                            "description": "Во сколько добор обходится сейчас (минимальные единицы LMC)."
                                        },
                                        "quoted_lmc_minor": {
                                            "type": "integer",
                                            "example": 360000000,
                                            "description": "Цена, на которую вы согласились ранее (минимальные единицы LMC)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "reason",
                                        "error_code",
                                        "actual_lmc_minor",
                                        "quoted_lmc_minor"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "The pay-as-you-go quote is stale — request a fresh one.",
                                        "reason": "quote_mismatch",
                                        "error_code": "quote_mismatch",
                                        "actual_lmc_minor": 380000000,
                                        "quoted_lmc_minor": 360000000
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/reseller/codes/{uuid}": {
            "get": {
                "operationId": "reseller.showCode",
                "description": "Один свой код с полным значением `code`. Чужой или несуществующий — 404 `gift_code_not_found`. **Право:** `reseller.read`.",
                "summary": "Show my gift code",
                "tags": [
                    "Public Reseller"
                ],
                "parameters": [
                    {
                        "name": "uuid",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID подарочного кода — 36 символов, не сам код вида `LUM-…`.\n\nБерётся из поля `uuid`: его возвращают `POST /reseller/codes` при выпуске и\n`GET /reseller/codes` в списке. Чужой или несуществующий код → `404` с\n`reason: gift_code_not_found`.",
                        "example": "0199b3a4-7c21-7d4e-9a8b-5f6e7d8c9b0a"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "`GiftCodeResource`",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/GiftCode",
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "data"
                                    ],
                                    "example": {
                                        "data": {
                                            "uuid": "string",
                                            "code_masked": "string",
                                            "code": "string",
                                            "nominal": "string",
                                            "nominal_formatted": 1,
                                            "cost": "string",
                                            "cost_formatted": 1,
                                            "discount_bps": 1,
                                            "source": "string",
                                            "funding": "string",
                                            "status": "string",
                                            "activatable_at": "string",
                                            "redeemed_at": "string",
                                            "cancelled_at": "string",
                                            "created_at": "string",
                                            "currency_asset": {
                                                "code": "string",
                                                "precision": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/reseller/codes/{uuid}/cancel": {
            "post": {
                "operationId": "reseller.cancelCode",
                "description": "Отмена неактивированного кода реселлера в окне после выпуска (`reseller_cancel_window_minutes` из настроек); стоимость возвращается на баланс реселлера. Вне окна — 409 `gift_code_cancel_window_expired`, уже активирован или отменён — 409 `gift_code_not_cancellable`. **Право:** `reseller.write`.",
                "summary": "Cancel my gift code",
                "tags": [
                    "Public Reseller"
                ],
                "parameters": [
                    {
                        "name": "uuid",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID подарочного кода — 36 символов, не сам код вида `LUM-…`.\n\nБерётся из поля `uuid`: его возвращают `POST /reseller/codes` при выпуске и\n`GET /reseller/codes` в списке. Чужой или несуществующий код → `404` с\n`reason: gift_code_not_found`.",
                        "example": "0199b3a4-7c21-7d4e-9a8b-5f6e7d8c9b0a"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "`GiftCodeResource`",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/GiftCode",
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "data"
                                    ],
                                    "example": {
                                        "data": {
                                            "uuid": "string",
                                            "code_masked": "string",
                                            "code": "string",
                                            "nominal": "string",
                                            "nominal_formatted": 1,
                                            "cost": "string",
                                            "cost_formatted": 1,
                                            "discount_bps": 1,
                                            "source": "string",
                                            "funding": "string",
                                            "status": "string",
                                            "activatable_at": "string",
                                            "redeemed_at": "string",
                                            "cancelled_at": "string",
                                            "created_at": "string",
                                            "currency_asset": {
                                                "code": "string",
                                                "precision": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/storage/url": {
            "post": {
                "operationId": "storage.url",
                "description": "Бэкенд выдаёт временную ссылку для скачивания файла-результата вашего заказа.\nПередайте `path` файла (как он указан в заказе) и получите ссылку, по которой\nфайл можно скачать обычным GET-запросом.\n\n**Параметры:**\n- `path` — путь к файлу из вашего заказа\n- `download` — при `true` ссылка помечается как вложение: файл скачается\n  под человекочитаемым именем заказа, а не откроется во вкладке\n\n**Безопасность:**\n- Ссылка действительна ограниченное время, затем истекает\n- Доступны только файлы ваших собственных заказов; запрос чужого файла вернёт 403",
                "summary": "Get File URL",
                "tags": [
                    "Public Storage"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/GetFileUrlRequest",
                                "example": {
                                    "path": "string",
                                    "download": true,
                                    "number_base": "0",
                                    "number_padding": 1
                                }
                            },
                            "examples": {
                                "result_file": {
                                    "summary": "Файл результата",
                                    "description": "Путь берётся целиком из result.files[] заказа.",
                                    "value": {
                                        "path": "storage/42/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/result/output.mp3"
                                    }
                                },
                                "download": {
                                    "summary": "Ссылка на скачивание",
                                    "description": "download: true — файл сохранится под именем заказа, а не откроется во вкладке.",
                                    "value": {
                                        "path": "storage/42/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/result/output.mp3",
                                        "download": true,
                                        "number_base": 1,
                                        "number_padding": 3
                                    }
                                }
                            }
                        }
                    },
                    "description": "Возвращает временную ссылку (`data.url`, TTL ~60 минут). Качайте её обычным `GET` —\nзаголовок `X-API-KEY` для скачивания не нужен и не передаётся."
                },
                "responses": {
                    "500": {
                        "description": "Не удалось выпустить ссылку. Повторите запрос — операция идемпотентна.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "anyOf": [
                                                {
                                                    "type": "null"
                                                },
                                                {
                                                    "type": "array",
                                                    "items": {}
                                                },
                                                {
                                                    "type": "string"
                                                },
                                                {
                                                    "type": "object"
                                                }
                                            ],
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Failed to generate the download link. Please try again later."
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к файлу",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "You do not have access to this file."
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Файл не найден: неверный `path` либо срок хранения заказа истёк.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` у ошибок."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Причина отказа словами, на языке аккаунта. Для показа пользователю; ветвиться по ней нельзя — формулировки меняются."
                                        },
                                        "errors": {
                                            "type": "null",
                                            "description": "Разбор по полям — только у ошибок валидации (`422`): ключ соответствует полю запроса, значение — список претензий к нему. У остальных кодов приходит `null`: разбирать нечего, причина целиком в `message`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "File not found."
                                    }
                                }
                            }
                        }
                    },
                    "200": {
                        "description": "Временная ссылка в `data.url`. Живёт около часа; качайте обычным `GET`, без `X-API-KEY`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "url": {
                                                    "type": "string",
                                                    "description": "Временная ссылка на файл. Качается обычным `GET` **без** заголовка `X-API-KEY`. Живёт ограниченное время — не сохраняйте её надолго, запрашивайте перед скачиванием."
                                                }
                                            },
                                            "required": [
                                                "url"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Download link generated.",
                                        "data": {
                                            "url": "https://s3.lumean.app/lumean/storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260717%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260717T150000Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий API-ключ",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/subscriptions": {
            "get": {
                "operationId": "subscription.index",
                "description": "Возвращает сводку по подпискам владельца API-ключа.\n\nВ ответе:\n- `available_models` — список ключей моделей, доступных пользователю;\n- `subscriptions` — только пригодные (живые) подписки: статус Active и срок\n  не истёк. Истёкшие/просроченные и отменённые не возвращаются. По каждой:\n  статус, признак `is_usable`, границы текущего периода и список `items` с\n  планом, периодом, датой окончания (`end_at`) и остатком токенов\n  (`token_allowance` / `tokens_used` / `tokens_remaining`).\n\nДенежные данные (кошельки, баланс, цены) в публичном API не отдаются.\n\nТребует право `billing.read`.",
                "summary": "Get subscriptions of API key owner",
                "tags": [
                    "Public Subscriptions"
                ],
                "responses": {
                    "200": {
                        "description": "Сводка подписок и доступных моделей",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "available_models": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "string"
                                                    },
                                                    "description": "Коды моделей, доступных по активным подпискам. Модель вне этого списка будет отклонена при создании заказа."
                                                },
                                                "subscriptions": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "id": {
                                                                "type": "integer",
                                                                "description": "Идентификатор подписки."
                                                            },
                                                            "status": {
                                                                "type": "string",
                                                                "description": "Состояние подписки. Автопродления нет: по истечении срока подписка переходит в истёкшую, и тариф покупается заново."
                                                            },
                                                            "current_period_start": {
                                                                "type": "string",
                                                                "description": "Начало текущего периода, ISO 8601."
                                                            },
                                                            "current_period_end": {
                                                                "type": "string",
                                                                "description": "Конец текущего периода, ISO 8601 — момент, когда подписка истечёт."
                                                            },
                                                            "items": {
                                                                "type": "array",
                                                                "items": {
                                                                    "type": "object",
                                                                    "properties": {
                                                                        "plan": {
                                                                            "type": "object",
                                                                            "properties": {
                                                                                "code": {
                                                                                    "type": "string",
                                                                                    "description": "Код тарифа."
                                                                                },
                                                                                "name": {
                                                                                    "type": "string",
                                                                                    "description": "Название тарифа для показа."
                                                                                }
                                                                            },
                                                                            "required": [
                                                                                "code",
                                                                                "name"
                                                                            ],
                                                                            "description": "Купленный тариф: код и название."
                                                                        },
                                                                        "status": {
                                                                            "type": "string",
                                                                            "description": "Состояние тарифа внутри подписки."
                                                                        },
                                                                        "is_usable": {
                                                                            "type": "boolean",
                                                                            "description": "Можно ли расходовать этот тариф прямо сейчас. **Ориентируйтесь на него**, а не на `status`: тариф бывает активен формально, но уже исчерпан."
                                                                        },
                                                                        "billing_period": {
                                                                            "type": "string",
                                                                            "description": "Период тарификации. Поддерживается только месячный."
                                                                        },
                                                                        "start_at": {
                                                                            "type": "string",
                                                                            "description": "Когда тариф начал действовать, ISO 8601."
                                                                        },
                                                                        "end_at": {
                                                                            "type": "string",
                                                                            "description": "Когда тариф истекает, ISO 8601. После этого момента остаток токенов сгорает."
                                                                        },
                                                                        "token_allowance": {
                                                                            "type": "integer",
                                                                            "description": "Сколько токенов даёт тариф за период."
                                                                        },
                                                                        "tokens_used": {
                                                                            "type": "integer",
                                                                            "description": "Сколько токенов израсходовано в текущем периоде."
                                                                        },
                                                                        "tokens_remaining": {
                                                                            "type": "integer",
                                                                            "description": "Сколько токенов осталось. Именно этот остаток тратится **до** списания LMC — доплата включается, только когда он исчерпан."
                                                                        }
                                                                    },
                                                                    "required": [
                                                                        "plan",
                                                                        "status",
                                                                        "is_usable",
                                                                        "billing_period",
                                                                        "start_at",
                                                                        "end_at",
                                                                        "token_allowance",
                                                                        "tokens_used",
                                                                        "tokens_remaining"
                                                                    ]
                                                                },
                                                                "description": "Тарифы внутри подписки: что куплено и сколько токенов осталось."
                                                            }
                                                        },
                                                        "required": [
                                                            "id",
                                                            "status",
                                                            "current_period_start",
                                                            "current_period_end",
                                                            "items"
                                                        ]
                                                    },
                                                    "description": "Ваши подписки. Пустой список — активной подписки нет: заказы возможны только при положительном балансе LMC."
                                                }
                                            },
                                            "required": [
                                                "available_models",
                                                "subscriptions"
                                            ],
                                            "description": "Полезная нагрузка ответа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "available_models": [
                                                "gpt-4.1-mini",
                                                "claude-haiku-4.5"
                                            ],
                                            "subscriptions": [
                                                {
                                                    "id": 301,
                                                    "status": "active",
                                                    "current_period_start": "2026-07-15T00:00:00+00:00",
                                                    "current_period_end": "2026-08-15T00:00:00+00:00",
                                                    "items": [
                                                        {
                                                            "plan": {
                                                                "code": "creator",
                                                                "name": "Creator"
                                                            },
                                                            "status": "active",
                                                            "is_usable": true,
                                                            "billing_period": "monthly",
                                                            "start_at": "2026-07-15T00:00:00+00:00",
                                                            "end_at": "2026-08-15T00:00:00+00:00",
                                                            "token_allowance": 30000,
                                                            "tokens_used": 8200,
                                                            "tokens_remaining": 21800
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "API-ключ отсутствует, неизвестен, неактивен или истёк",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права billing.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/templates/browse": {
            "get": {
                "operationId": "template.browse",
                "description": "Постраничный обход дерева шаблонов: папки и шаблоны в одном списке `data.items[]`,\nпапки идут первыми.\n\nНе передан `folder_id` — отдаётся корень. Чтобы спуститься в папку, возьмите её `id`\nиз `items[]` и повторите запрос с `?folder_id=<id>`; путь до текущей папки приходит\nв `data.breadcrumbs`, сама папка — в `data.current_folder`.\n\nПагинация — в `data.current_page` / `last_page` / `per_page` / `total`.\n\n**Требуемое право ключа:** `templates.read`.",
                "summary": "Browse Templates",
                "tags": [
                    "Public Templates"
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        },
                        "description": "Номер страницы, начиная с `1`.",
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100
                        },
                        "description": "Элементов на странице, `1..100`. По умолчанию 15.",
                        "example": 15
                    },
                    {
                        "name": "sort_by",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "enum": [
                                "name",
                                "created_at"
                            ]
                        },
                        "description": "Поле сортировки. По умолчанию `name`."
                    },
                    {
                        "name": "sort_order",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "enum": [
                                "asc",
                                "desc"
                            ]
                        },
                        "description": "Направление сортировки. По умолчанию `asc`."
                    },
                    {
                        "name": "folder_id",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "format": "uuid"
                        },
                        "description": "UUID папки. Не передан — отдаётся корень (`folder_id = null`). Хлебные крошки текущей папки приходят в `data.breadcrumbs`."
                    },
                    {
                        "name": "task_type",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "maxLength": 64
                        },
                        "description": "Показать только шаблоны сервисов этого типа задачи (`default_task_type` сервиса), например `tts` — все движки озвучки сразу. Пустые папки при фильтре скрываются.",
                        "example": "tts"
                    },
                    {
                        "name": "service",
                        "in": "query",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ]
                        },
                        "description": "Показать только шаблоны этого сервиса. Код — короткая латинская строка: `elevenlabs`, `heygen`, `lumimg`, `sfx`, `music`; доступные вам перечисляет `GET /usage` в поле `service_code`.",
                        "example": "elevenlabs"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Список папок и шаблонов",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/BrowseTemplates",
                                            "description": "Содержимое папки с пагинацией и цепочкой предков — для дерева шаблонов в интерфейсе."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Templates browsed",
                                        "data": {
                                            "items": [
                                                {
                                                    "type": "template",
                                                    "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                                    "name": "Narrator Voice (EN)",
                                                    "slug": null,
                                                    "service_key": "elevenlabs",
                                                    "service": {
                                                        "id": 3,
                                                        "code": "elevenlabs",
                                                        "name": "ElevenLabs",
                                                        "display_name": "ElevenLabs TTS",
                                                        "payg_access": "everyone",
                                                        "billing_mode": "pay_as_you_go",
                                                        "unit_asset_id": 5,
                                                        "unit_asset": {
                                                            "id": 5,
                                                            "code": "tts_units",
                                                            "kind": "unit",
                                                            "precision": 0,
                                                            "name": "TTS Units",
                                                            "deleted_at": null,
                                                            "created_at": "2026-01-10T09:00:00+00:00",
                                                            "updated_at": "2026-01-10T09:00:00+00:00"
                                                        },
                                                        "required_entitlement_id": null,
                                                        "units_per_call": null,
                                                        "units_per_char": 1,
                                                        "units_per_image": null,
                                                        "units_per_second": null,
                                                        "min_billable_chars": 1,
                                                        "lumcoin_per_token": 10,
                                                        "lumc_precision": 2,
                                                        "price_lumc_minor": null,
                                                        "unit_size": 1,
                                                        "uses_money_first_pricing": false,
                                                        "rate_unit": "char",
                                                        "display_category": "voice",
                                                        "is_premium": false,
                                                        "display_order": 1,
                                                        "metadata": null,
                                                        "is_active": true,
                                                        "created_at": "2026-01-10T09:00:00+00:00",
                                                        "updated_at": "2026-01-10T09:00:00+00:00"
                                                    },
                                                    "config": {
                                                        "tts_settings": {
                                                            "model_id": "eleven_multilingual_v2",
                                                            "voice_id": "21m00Tcm4TlvDq8ikWAM",
                                                            "mode": "mode_v1",
                                                            "language_code": null,
                                                            "advanced_voice_settings": true,
                                                            "voice_settings": {
                                                                "stability": 0.5,
                                                                "similarity_boost": 0.75,
                                                                "use_speaker_boost": true
                                                            }
                                                        }
                                                    },
                                                    "is_public": false,
                                                    "folder_id": null,
                                                    "created_at": "2026-06-01T10:00:00+00:00",
                                                    "updated_at": "2026-06-01T10:00:00+00:00"
                                                },
                                                {
                                                    "type": "folder",
                                                    "id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                                                    "name": "Podcast Intros",
                                                    "templates_count": 3,
                                                    "created_at": "2026-05-01T09:00:00+00:00",
                                                    "updated_at": "2026-05-01T09:00:00+00:00"
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 15,
                                            "total": 2,
                                            "current_folder": null,
                                            "breadcrumbs": [
                                                {
                                                    "id": null,
                                                    "name": "Root"
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права templates.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/templates/config-options": {
            "get": {
                "operationId": "template.getConfigOptions",
                "description": "Справочник допустимых значений `config` для конкретного сервиса: что можно положить\nв шаблон и в `config_override` заказа.\n\n> Справочник описывает **настройки сервиса** и покрывает не весь `config`. Позаказный\n> режим генерации (`generation_mode`, абзацная озвучка) задаётся отдельно и здесь не\n> перечислен — он описан у поля `config_override` в `POST /orders`.\n\nОтвет — карта дескрипторов полей. У каждого поля приходят `type`, `default`, `nullable`\nи, в зависимости от типа, `options` (список допустимых значений), `min`/`max`/`step`\n(диапазон). Отдельная секция `conditional_overrides` описывает сужения, которые\nвключаются при определённом выборе: например, для не-v3 моделей ElevenLabs список\nязыков сокращается с 73 до 29, а на v3-семействе `stability` принимает только\n`0.0`, `0.5` и `1.0`.\n\nСверяйтесь с этим ответом вместо того, чтобы угадывать: значение вне допустимого\nнабора отклоняется валидацией (`422`) при создании шаблона или заказа.\n\n### `heygen`: `voice_engine` — набор СЕРВИСА, а не голоса\n\nПеречисленные здесь `options` — то, что поддерживает **сервис**. Какие из них\nподдерживает конкретный голос — свойство голоса: смотрите `voice_engines` у элемента\nкаталога `GET /voices/heygen` и берите значение, которое есть в обоих списках. Из 2330\nголосов каталога `elevenLabs` поддерживают 1947, а 383 не поддерживают ни одного из\nперечисленных здесь движков.\n\nНесовпадающая пара «голос + движок» отклоняется с `422` в поле\n`config.tts_settings.voice_id`.\n\n### Группировка по секциям — не всегда путь в конфиге\n\nОтвет всегда сгруппирован: имя секции, внутри — её поля. У `elevenlabs`, `heygen` и\n`lumimg` это же имя является и путём (`tts_settings.model_id`,\n`image_settings.aspect_ratio`), поэтому структуру можно повторять как есть. А у `sfx`,\n`music` и `voice_search` секция чисто оформительская: настоящие ключи лежат в корне.\nДля них берите из ответа **имена полей и их диапазоны, но не обёртку**:\n\n| Сервис | Показано в справочнике | Принимается в `config` и `task_data` |\n| --- | --- | --- |\n| `elevenlabs` | `tts_settings` → `model_id` | `{\"tts_settings\": {\"model_id\": …}}` |\n| `sfx` | `sfx_settings` → `text` | `{\"text\": …}` |\n| `music` | `music_settings` → `prompt` | `{\"prompt\": …}` |\n| `voice_search` | `voice_search_settings` → `top` | `{\"top\": …}` |\n\nПрисланная обёртка в шаблоне даёт `422` («поле обязательно» — обязательный ключ\nоказался спрятан), а в `config_override` заказа отбрасывается молча.\n\n**Требуемое право ключа:** `templates.read`.",
                "summary": "Get Template Config Options",
                "tags": [
                    "Public Templates"
                ],
                "parameters": [
                    {
                        "name": "service",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "**Обязателен.** Код сервиса, для которого нужны опции конфига: короткая латинская\nстрока — `elevenlabs`, `heygen`, `lumimg`, `sfx`, `music`. Доступные вам коды\nперечисляет `GET /usage` в поле `service_code`.\n\nОтвет — карта дескрипторов полей (`type`, `options`, `min`, `max`, `step`, `default`,\n`nullable`, `conditional_overrides`): допустимые модели, голоса, диапазоны настроек.\nСверяйтесь с ним вместо того, чтобы угадывать.\n\nСправочник описывает **настройки сервиса** и покрывает не весь `config`:\nпозаказный режим генерации (`generation_mode`) в него не входит — он описан\nу поля `config_override` в `POST /orders`.",
                        "example": "elevenlabs"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Опции конфигурации",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "object",
                                            "additionalProperties": {},
                                            "description": "Карта дескрипторов: ключ — имя поля `config`, значение — что в него можно\nположить. У дескриптора всегда есть `type`, `default` и `nullable`;\nдальше по типу — `options` (список допустимых значений) либо\n`min`/`max`/`step` (диапазон).\n\nОтдельный ключ `conditional_overrides` описывает сужения, включающиеся\nпри определённом выборе: например, на v3-моделях ElevenLabs `stability`\nпринимает только `0.0`, `0.5` и `1.0`, а список языков у не-v3 моделей\nкороче.\n\nПустой объект — у сервиса нет настраиваемого `config` (так у клонирования\nголоса)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Template config options retrieved",
                                        "data": {
                                            "tts_settings": {
                                                "model_id": {
                                                    "type": "select",
                                                    "options": [
                                                        "eleven_v4",
                                                        "eleven_v4_exp",
                                                        "eleven_v3",
                                                        "eleven_v3_beta",
                                                        "eleven_multilingual_v2",
                                                        "eleven_flash_v2_5",
                                                        "eleven_turbo_v2_5",
                                                        "eleven_turbo_v2",
                                                        "eleven_flash_v2"
                                                    ],
                                                    "default": "eleven_multilingual_v2"
                                                },
                                                "public_owner_id": {
                                                    "type": "string",
                                                    "default": null,
                                                    "nullable": true
                                                },
                                                "voice_id": {
                                                    "type": "string"
                                                },
                                                "mode": {
                                                    "type": "select",
                                                    "options": [
                                                        "mode_v1"
                                                    ],
                                                    "default": "mode_v1"
                                                },
                                                "language_code": {
                                                    "type": "select",
                                                    "options": [
                                                        "en",
                                                        "ja",
                                                        "zh",
                                                        "de",
                                                        "hi",
                                                        "fr",
                                                        "ko",
                                                        "pt",
                                                        "it",
                                                        "es",
                                                        "id",
                                                        "nl",
                                                        "tr",
                                                        "fil",
                                                        "pl",
                                                        "sv",
                                                        "bg",
                                                        "ro",
                                                        "ar",
                                                        "cs",
                                                        "el",
                                                        "fi",
                                                        "hr",
                                                        "ms",
                                                        "sk",
                                                        "da",
                                                        "ta",
                                                        "uk",
                                                        "ru",
                                                        "af",
                                                        "hy",
                                                        "as",
                                                        "az",
                                                        "be",
                                                        "bn",
                                                        "bs",
                                                        "ca",
                                                        "ceb",
                                                        "ny",
                                                        "et",
                                                        "gl",
                                                        "ka",
                                                        "gu",
                                                        "ha",
                                                        "he",
                                                        "hu",
                                                        "is",
                                                        "ga",
                                                        "jv",
                                                        "kn",
                                                        "kk",
                                                        "ky",
                                                        "lv",
                                                        "ln",
                                                        "lt",
                                                        "lb",
                                                        "mk",
                                                        "ml",
                                                        "mr",
                                                        "ne",
                                                        "no",
                                                        "or",
                                                        "ps",
                                                        "fa",
                                                        "pa",
                                                        "sr",
                                                        "sl",
                                                        "sw",
                                                        "te",
                                                        "th",
                                                        "ur",
                                                        "vi",
                                                        "cy"
                                                    ],
                                                    "default": null,
                                                    "nullable": true
                                                },
                                                "advanced_voice_settings": {
                                                    "type": "boolean",
                                                    "default": false,
                                                    "nullable": true
                                                },
                                                "prev_next_text": {
                                                    "type": "boolean",
                                                    "default": false,
                                                    "nullable": true
                                                },
                                                "request_ids": {
                                                    "type": "boolean",
                                                    "default": false,
                                                    "nullable": true
                                                },
                                                "apply_text_normalization": {
                                                    "type": "select",
                                                    "options": [
                                                        "auto",
                                                        "on",
                                                        "off"
                                                    ],
                                                    "default": "auto",
                                                    "nullable": true
                                                },
                                                "voice_settings": {
                                                    "stability": {
                                                        "type": "range",
                                                        "default": 0.5,
                                                        "min": 0,
                                                        "max": 1,
                                                        "step": 0.1,
                                                        "conditional_overrides": [
                                                            {
                                                                "when": {
                                                                    "operator": "and",
                                                                    "rules": [
                                                                        {
                                                                            "field": "tts_settings.model_id",
                                                                            "op": "eq",
                                                                            "value": "eleven_v3"
                                                                        }
                                                                    ]
                                                                },
                                                                "set": {
                                                                    "min": 0,
                                                                    "max": 1,
                                                                    "step": 0.5,
                                                                    "default": 0.5
                                                                }
                                                            },
                                                            {
                                                                "when": {
                                                                    "operator": "and",
                                                                    "rules": [
                                                                        {
                                                                            "field": "tts_settings.model_id",
                                                                            "op": "eq",
                                                                            "value": "eleven_v3_beta"
                                                                        }
                                                                    ]
                                                                },
                                                                "set": {
                                                                    "min": 0,
                                                                    "max": 1,
                                                                    "step": 0.5,
                                                                    "default": 0.5
                                                                }
                                                            }
                                                        ]
                                                    },
                                                    "similarity_boost": {
                                                        "type": "range",
                                                        "default": 0.75,
                                                        "min": 0,
                                                        "max": 1,
                                                        "step": 0.01
                                                    },
                                                    "style": {
                                                        "type": "range",
                                                        "default": 0,
                                                        "nullable": true,
                                                        "min": 0,
                                                        "max": 1,
                                                        "step": 0.01
                                                    },
                                                    "use_speaker_boost": {
                                                        "type": "boolean",
                                                        "default": true
                                                    },
                                                    "speed": {
                                                        "type": "range",
                                                        "default": 1,
                                                        "min": 0.7,
                                                        "max": 1.2,
                                                        "step": 0.01
                                                    }
                                                }
                                            },
                                            "pause_settings": {
                                                "enabled": {
                                                    "type": "boolean",
                                                    "default": false
                                                },
                                                "max_pause_symbols": {
                                                    "type": "integer",
                                                    "default": 500,
                                                    "min": 1,
                                                    "max": 20000
                                                },
                                                "default_duration": {
                                                    "type": "range",
                                                    "default": 0.5,
                                                    "min": 0.1,
                                                    "max": 10,
                                                    "step": 0.1
                                                },
                                                "auto_paragraph_pause": {
                                                    "type": "boolean",
                                                    "default": false
                                                },
                                                "max_shift": {
                                                    "type": "range",
                                                    "default": 0.1,
                                                    "nullable": true,
                                                    "min": 0,
                                                    "max": 2,
                                                    "step": 0.01
                                                },
                                                "min_anchor_silence": {
                                                    "type": "number",
                                                    "default": 1.0e-5,
                                                    "nullable": true,
                                                    "min": 0,
                                                    "max": 1,
                                                    "step": 1.0e-5
                                                }
                                            },
                                            "stress_settings": {
                                                "enabled": {
                                                    "type": "boolean",
                                                    "default": false
                                                }
                                            },
                                            "output_settings": {
                                                "audio_format": {
                                                    "type": "select",
                                                    "options": [
                                                        "mp3",
                                                        "wav",
                                                        "ogg"
                                                    ],
                                                    "default": "mp3",
                                                    "nullable": true
                                                },
                                                "audio_quality": {
                                                    "type": "select",
                                                    "options": [
                                                        "low",
                                                        "medium",
                                                        "high",
                                                        "lossless"
                                                    ],
                                                    "default": "high",
                                                    "nullable": true
                                                },
                                                "sample_rate": {
                                                    "type": "select",
                                                    "options": [
                                                        22050,
                                                        44100,
                                                        48000
                                                    ],
                                                    "default": 44100,
                                                    "nullable": true
                                                },
                                                "crossfade_ms": {
                                                    "type": "integer",
                                                    "default": 0,
                                                    "nullable": true,
                                                    "min": 0,
                                                    "max": 5000
                                                },
                                                "loudnorm": {
                                                    "enabled": {
                                                        "type": "boolean",
                                                        "default": false,
                                                        "nullable": true
                                                    },
                                                    "integrated_loudness": {
                                                        "type": "number",
                                                        "default": -16,
                                                        "nullable": true,
                                                        "min": -70,
                                                        "max": 0,
                                                        "step": 0.1
                                                    },
                                                    "true_peak": {
                                                        "type": "number",
                                                        "default": -1.5,
                                                        "nullable": true,
                                                        "min": -9,
                                                        "max": 0,
                                                        "step": 0.1
                                                    },
                                                    "loudness_range": {
                                                        "type": "number",
                                                        "default": 11,
                                                        "nullable": true,
                                                        "min": 1,
                                                        "max": 50,
                                                        "step": 0.1
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права templates.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/templates": {
            "get": {
                "operationId": "template.index",
                "description": "Постраничный список шаблонов владельца ключа: по умолчанию — **корневые** (без папки),\nновые первыми, **не больше 30 на странице**.\n\nШаблон — сохранённый набор настроек генерации: сервис, голос, модель, формат вывода.\nЕго `id` подставляется в `template_id` при создании заказа. Заводить шаблон на каждый\nзаказ не нужно: один шаблон переиспользуется, а настройки под конкретный заказ меняет\n`config_override` в `POST /orders`.\n\nОтвет: `data.items[]` — шаблоны, `data.current_page` / `last_page` / `per_page` /\n`total` — пагинация. Обход всех страниц: `page=1, 2, …` до `current_page = last_page`.\nНайти шаблон по названию — `search`, по сервису — `service`, внутри папки —\n`folder_id`; папки вместе с шаблонами — `GET /templates/browse`.\n\n**Прежний режим (устарел).** Запрос **без единого параметра** по-прежнему отдаёт все\nкорневые шаблоны одним массивом в `data` и несёт заголовки `Deprecation` и\n`Sunset` (дата снятия). После неё такой запрос получит первую страницу. Переход —\nдобавить `?page=1` и читать `data.items`.\n\n**Требуемое право ключа:** `templates.read`.",
                "summary": "List Templates",
                "tags": [
                    "Public Templates"
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "description": "Номер страницы, начиная с `1`. Любой параметр списка включает постраничный ответ; запрос совсем без параметров — устаревший полный список (до 06.11.2026).",
                        "schema": {
                            "type": "integer",
                            "default": 1
                        },
                        "example": 1
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "description": "Шаблонов на странице, `1..30`. По умолчанию 30.",
                        "schema": {
                            "type": "integer",
                            "default": 30
                        },
                        "example": 30
                    },
                    {
                        "name": "sort_by",
                        "in": "query",
                        "description": "Поле сортировки. По умолчанию `created_at` (новые первыми при `sort_order=desc`).",
                        "schema": {
                            "type": "string",
                            "default": "created_at",
                            "enum": [
                                "created_at",
                                "name"
                            ]
                        }
                    },
                    {
                        "name": "sort_order",
                        "in": "query",
                        "description": "Направление сортировки. По умолчанию `desc`.",
                        "schema": {
                            "type": "string",
                            "default": "desc",
                            "enum": [
                                "asc",
                                "desc"
                            ]
                        }
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "description": "Подстрока названия шаблона. Регистр не учитывается.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "service",
                        "in": "query",
                        "description": "Показать только шаблоны этого сервиса. Код — короткая латинская строка: `elevenlabs`, `heygen`, `cartesia`, `sfx`, `music`.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "elevenlabs"
                    },
                    {
                        "name": "folder_id",
                        "in": "query",
                        "description": "UUID папки. Не передан — шаблоны корня (`folder_id = null`).",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "`TemplateCollection`",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/TemplateList",
                                            "description": "Страница шаблонов владельца ключа (не больше 30): `items` и мета пагинации. Обход с папками — `GET /templates/browse`."
                                        }
                                    },
                                    "required": [
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Templates retrieved",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                                    "user_id": 501,
                                                    "slug": null,
                                                    "name": "Narrator Voice (EN)",
                                                    "service_key": "elevenlabs",
                                                    "service": {
                                                        "id": 3,
                                                        "code": "elevenlabs",
                                                        "name": "ElevenLabs",
                                                        "display_name": "ElevenLabs TTS",
                                                        "payg_access": "everyone",
                                                        "billing_mode": "pay_as_you_go",
                                                        "unit_asset_id": 5,
                                                        "unit_asset": {
                                                            "id": 5,
                                                            "code": "tts_units",
                                                            "kind": "unit",
                                                            "precision": 0,
                                                            "name": "TTS Units",
                                                            "deleted_at": null,
                                                            "created_at": "2026-01-10T09:00:00+00:00",
                                                            "updated_at": "2026-01-10T09:00:00+00:00"
                                                        },
                                                        "required_entitlement_id": null,
                                                        "units_per_call": null,
                                                        "units_per_char": 1,
                                                        "units_per_image": null,
                                                        "units_per_second": null,
                                                        "min_billable_chars": 1,
                                                        "lumcoin_per_token": 10,
                                                        "lumc_precision": 2,
                                                        "price_lumc_minor": null,
                                                        "unit_size": 1,
                                                        "uses_money_first_pricing": false,
                                                        "rate_unit": "char",
                                                        "display_category": "voice",
                                                        "is_premium": false,
                                                        "display_order": 1,
                                                        "metadata": null,
                                                        "is_active": true,
                                                        "created_at": "2026-01-10T09:00:00+00:00",
                                                        "updated_at": "2026-01-10T09:00:00+00:00"
                                                    },
                                                    "config": {
                                                        "tts_settings": {
                                                            "model_id": "eleven_multilingual_v2",
                                                            "voice_id": "21m00Tcm4TlvDq8ikWAM",
                                                            "mode": "mode_v1",
                                                            "language_code": null,
                                                            "advanced_voice_settings": true,
                                                            "voice_settings": {
                                                                "stability": 0.5,
                                                                "similarity_boost": 0.75,
                                                                "use_speaker_boost": true
                                                            }
                                                        }
                                                    },
                                                    "is_public": false,
                                                    "folder_id": null,
                                                    "created_at": "2026-06-01T10:00:00+00:00",
                                                    "updated_at": "2026-06-01T10:00:00+00:00"
                                                },
                                                {
                                                    "id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
                                                    "user_id": 501,
                                                    "slug": null,
                                                    "name": "Podcast Host (RU)",
                                                    "service_key": "elevenlabs",
                                                    "service": {
                                                        "id": 3,
                                                        "code": "elevenlabs",
                                                        "name": "ElevenLabs",
                                                        "display_name": "ElevenLabs TTS",
                                                        "payg_access": "everyone",
                                                        "billing_mode": "pay_as_you_go",
                                                        "unit_asset_id": 5,
                                                        "unit_asset": {
                                                            "id": 5,
                                                            "code": "tts_units",
                                                            "kind": "unit",
                                                            "precision": 0,
                                                            "name": "TTS Units",
                                                            "deleted_at": null,
                                                            "created_at": "2026-01-10T09:00:00+00:00",
                                                            "updated_at": "2026-01-10T09:00:00+00:00"
                                                        },
                                                        "required_entitlement_id": null,
                                                        "units_per_call": null,
                                                        "units_per_char": 1,
                                                        "units_per_image": null,
                                                        "units_per_second": null,
                                                        "min_billable_chars": 1,
                                                        "lumcoin_per_token": 10,
                                                        "lumc_precision": 2,
                                                        "price_lumc_minor": null,
                                                        "unit_size": 1,
                                                        "uses_money_first_pricing": false,
                                                        "rate_unit": "char",
                                                        "display_category": "voice",
                                                        "is_premium": false,
                                                        "display_order": 1,
                                                        "metadata": null,
                                                        "is_active": true,
                                                        "created_at": "2026-01-10T09:00:00+00:00",
                                                        "updated_at": "2026-01-10T09:00:00+00:00"
                                                    },
                                                    "config": {
                                                        "tts_settings": {
                                                            "model_id": "eleven_multilingual_v2",
                                                            "voice_id": "AZnzlk1XvdvUeBnXmlld",
                                                            "mode": "mode_v1",
                                                            "language_code": "ru",
                                                            "advanced_voice_settings": true,
                                                            "voice_settings": {
                                                                "stability": 0.4,
                                                                "similarity_boost": 0.8,
                                                                "use_speaker_boost": true
                                                            }
                                                        }
                                                    },
                                                    "is_public": false,
                                                    "folder_id": null,
                                                    "created_at": "2026-06-10T08:00:00+00:00",
                                                    "updated_at": "2026-06-10T08:00:00+00:00"
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 30,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права templates.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "template.store",
                "description": "Создаёт шаблон генерации от имени владельца ключа. В ответе (`201`) приходит готовый\nшаблон — его `id` подставляйте в `template_id` при создании заказов.\n\n`config` валидируется по схеме сервиса, указанного в `service_key`. Допустимые ключи,\nсписки значений и диапазоны берите из `GET /templates/config-options?service=<код>` —\nзначение вне схемы отклоняется с `422`.\n\n**Требуемое право ключа:** `templates.write`.",
                "summary": "Create Template",
                "tags": [
                    "Public Templates"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/StoreTemplateRequest",
                                "example": {
                                    "folder_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                    "service_key": "string",
                                    "name": "string",
                                    "is_public": true,
                                    "config": []
                                }
                            },
                            "examples": {
                                "elevenlabs": {
                                    "summary": "Шаблон TTS (ElevenLabs)",
                                    "description": "Минимальный валидный config. Допустимые модели, голоса и диапазоны — GET /templates/config-options?service=elevenlabs.",
                                    "value": {
                                        "service_key": "elevenlabs",
                                        "name": "Диктор — женский, спокойный",
                                        "config": {
                                            "tts_settings": {
                                                "mode": "mode_v1",
                                                "model_id": "eleven_multilingual_v2",
                                                "voice_id": "EXAVITQu4vr4xnSDxMaL",
                                                "language_code": "ru",
                                                "advanced_voice_settings": true,
                                                "voice_settings": {
                                                    "stability": 0.5,
                                                    "similarity_boost": 0.75,
                                                    "use_speaker_boost": true,
                                                    "speed": 1
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "description": "`config` валидируется по схеме сервиса из `service_key`. Допустимые ключи, списки\nзначений и диапазоны — в `GET /templates/config-options?service=<код>`; значение\nвне схемы отклоняется с `422`."
                },
                "responses": {
                    "201": {
                        "description": "Шаблон создан. Его `id` подставляйте в `template_id` при создании заказов.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Template",
                                            "description": "Созданный шаблон. Его `id` кладут в `template_id` при создании заказа."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Template created",
                                        "data": {
                                            "id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
                                            "user_id": 501,
                                            "slug": "2f3a4b5c-6d7e-4f8a-9b0c-1d2e3f4a5b6c",
                                            "name": "Ad Voice (RU)",
                                            "service_key": "elevenlabs",
                                            "config": {
                                                "tts_settings": {
                                                    "model_id": "eleven_multilingual_v2",
                                                    "voice_id": "pNInz6obpgDQGcFmaJgB",
                                                    "mode": "mode_v1",
                                                    "language_code": "ru",
                                                    "advanced_voice_settings": true,
                                                    "voice_settings": {
                                                        "stability": 0.6,
                                                        "similarity_boost": 0.7,
                                                        "use_speaker_boost": true
                                                    }
                                                }
                                            },
                                            "is_public": false,
                                            "folder_id": null,
                                            "created_at": "2026-07-17T15:30:00+00:00",
                                            "updated_at": "2026-07-17T15:30:00+00:00"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка валидации",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права templates.write",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/templates/{template}": {
            "get": {
                "operationId": "template.show",
                "description": "Возвращает один шаблон целиком, вместе с `config` и сервисом, к которому он привязан.\n\nЧужой шаблон недоступен даже по прямой ссылке — ответ `403`.\n\n**Требуемое право ключа:** `templates.read`.",
                "summary": "Get Template",
                "tags": [
                    "Public Templates"
                ],
                "parameters": [
                    {
                        "name": "template",
                        "in": "path",
                        "required": true,
                        "description": "UUID шаблона — 36 символов, не число.\n\nБерётся из поля `id`: его возвращает `POST /templates` при создании, а также\n`GET /templates` и `GET /templates/browse` в списках.",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "example": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Информация о шаблоне",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Template",
                                            "description": "Шаблон целиком, вместе с текущим `config`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Template retrieved",
                                        "data": {
                                            "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                            "user_id": 501,
                                            "slug": null,
                                            "name": "Narrator Voice (EN)",
                                            "service_key": "elevenlabs",
                                            "service": {
                                                "id": 3,
                                                "code": "elevenlabs",
                                                "name": "ElevenLabs",
                                                "display_name": "ElevenLabs TTS",
                                                "payg_access": "everyone",
                                                "billing_mode": "pay_as_you_go",
                                                "unit_asset_id": 5,
                                                "unit_asset": {
                                                    "id": 5,
                                                    "code": "tts_units",
                                                    "kind": "unit",
                                                    "precision": 0,
                                                    "name": "TTS Units",
                                                    "deleted_at": null,
                                                    "created_at": "2026-01-10T09:00:00+00:00",
                                                    "updated_at": "2026-01-10T09:00:00+00:00"
                                                },
                                                "required_entitlement_id": null,
                                                "units_per_call": null,
                                                "units_per_char": 1,
                                                "units_per_image": null,
                                                "units_per_second": null,
                                                "min_billable_chars": 1,
                                                "lumcoin_per_token": 10,
                                                "lumc_precision": 2,
                                                "price_lumc_minor": null,
                                                "unit_size": 1,
                                                "uses_money_first_pricing": false,
                                                "rate_unit": "char",
                                                "display_category": "voice",
                                                "is_premium": false,
                                                "display_order": 1,
                                                "metadata": null,
                                                "is_active": true,
                                                "created_at": "2026-01-10T09:00:00+00:00",
                                                "updated_at": "2026-01-10T09:00:00+00:00"
                                            },
                                            "config": {
                                                "tts_settings": {
                                                    "model_id": "eleven_multilingual_v2",
                                                    "voice_id": "21m00Tcm4TlvDq8ikWAM",
                                                    "mode": "mode_v1",
                                                    "language_code": null,
                                                    "advanced_voice_settings": true,
                                                    "voice_settings": {
                                                        "stability": 0.5,
                                                        "similarity_boost": 0.75,
                                                        "use_speaker_boost": true
                                                    }
                                                }
                                            },
                                            "is_public": false,
                                            "folder_id": null,
                                            "created_at": "2026-06-01T10:00:00+00:00",
                                            "updated_at": "2026-06-01T10:00:00+00:00"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к шаблону",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Шаблон не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "put": {
                "operationId": "template.update",
                "description": "Частичное обновление шаблона: присылайте только те поля, которые меняете. Метод\nпринимает и `PUT`, и `PATCH` — поведение одинаковое.\n\n`config` заменяется **целиком**, а не мержится: чтобы поменять одну настройку,\nпришлите весь объект с ней. `service_key` меняется только вместе с `config` — иначе\nконфиг не пройдёт валидацию по схеме нового сервиса.\n\nИзменение шаблона не затрагивает уже созданные заказы: заказ выполняется, повторяется\n(`POST /orders/{order}/retry`) и показывается (`template.config` в `GET /orders/{order}`)\nс настройками на момент своего создания. Исключение — заказы, созданные до конца\nсентября 2026 года: снимка настроек у них нет, они идут по текущему конфигу шаблона\n(с наложенным `config_override`), и правка шаблона их затрагивает.\n\n**Требуемое право ключа:** `templates.write`.",
                "summary": "Update Template",
                "tags": [
                    "Public Templates"
                ],
                "parameters": [
                    {
                        "name": "template",
                        "in": "path",
                        "required": true,
                        "description": "UUID шаблона — 36 символов, не число.\n\nБерётся из поля `id`: его возвращает `POST /templates` при создании, а также\n`GET /templates` и `GET /templates/browse` в списках.",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "example": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UpdateTemplateRequest",
                                "example": {
                                    "folder_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                    "service_key": "string",
                                    "name": "string",
                                    "is_public": true,
                                    "config": []
                                }
                            },
                            "examples": {
                                "rename": {
                                    "summary": "Только переименовать",
                                    "description": "Частичное обновление: передаются лишь изменяемые поля, остальные настройки шаблона сохраняются.",
                                    "value": {
                                        "name": "Диктор — женский, тёплый"
                                    }
                                },
                                "tweak_config": {
                                    "summary": "Подправить настройки голоса",
                                    "description": "config сливается с текущим вглубь — прислать можно только меняемые ключи. service_key при этом обязателен: одно без другого валидация не принимает.",
                                    "value": {
                                        "service_key": "elevenlabs",
                                        "config": {
                                            "tts_settings": {
                                                "voice_settings": {
                                                    "stability": 0.7,
                                                    "speed": 1.1
                                                }
                                            }
                                        }
                                    }
                                },
                                "switch_service": {
                                    "summary": "Сменить сервис",
                                    "description": "Присылайте полный config нового сервиса: старые ключи не исчезают (слияние идёт вглубь), но по схеме нового сервиса они уже ничего не значат.",
                                    "value": {
                                        "service_key": "heygen",
                                        "config": {
                                            "tts_settings": {
                                                "voice_id": "1bd001e7e50f421d891986aad5158bc8",
                                                "speed": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "description": "Обновление частичное: присылайте только меняемые поля, остальные сохранятся.\n\nДве особенности, из-за которых запрос чаще всего уходит в `422`:\n\n- **`service_key` и `config` ходят парой.** Одно без другого валидация не\n  принимает — даже когда сервис вы не меняете, его код придётся продублировать.\n- **`config` сливается с текущим вглубь, а не заменяет его.** Ключ, который вы\n  не прислали, останется прежним; вернуть его к значению по умолчанию правкой\n  шаблона нельзя — только пересозданием."
                },
                "responses": {
                    "200": {
                        "description": "Шаблон обновлён",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Template",
                                            "description": "Шаблон после обновления — с уже слитым `config`. Сверьтесь с ним, если присылали частичные настройки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Template updated",
                                        "data": {
                                            "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                            "user_id": 501,
                                            "slug": null,
                                            "name": "Narrator Voice (EN) v2",
                                            "service_key": "elevenlabs",
                                            "config": {
                                                "tts_settings": {
                                                    "model_id": "eleven_multilingual_v2",
                                                    "voice_id": "21m00Tcm4TlvDq8ikWAM",
                                                    "mode": "mode_v1",
                                                    "language_code": "en",
                                                    "advanced_voice_settings": true,
                                                    "voice_settings": {
                                                        "stability": 0.6,
                                                        "similarity_boost": 0.75,
                                                        "use_speaker_boost": true
                                                    }
                                                }
                                            },
                                            "is_public": false,
                                            "folder_id": null,
                                            "created_at": "2026-06-01T10:00:00+00:00",
                                            "updated_at": "2026-07-17T16:45:00+00:00"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к шаблону",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка валидации",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Шаблон не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "delete": {
                "operationId": "template.destroy",
                "description": "Удаляет шаблон владельца ключа. **Необратимо**, подтверждения не спрашивает: шаблон\nпропадает из `GET /templates` и `GET /templates/browse`, запросы к\n`/templates/{template}` отвечают `404`, а новый заказ или превью чанков с этим\n`template_id` — `422`, как на несуществующий шаблон.\n\nРанее созданные по шаблону заказы и их результаты остаются на месте и выполняются с\nнастройками на момент своего создания (заказы, созданные до конца сентября 2026 года,\nснимка не имеют — они выполняются по конфигу шаблона на момент удаления). Если заказы\nпо шаблону есть, он сохраняется скрытым: `GET /orders/{order}` по-прежнему показывает\nего в поле `template` — с `config` этого заказа.\n\n**Требуемое право ключа:** `templates.write`.",
                "summary": "Delete Template",
                "tags": [
                    "Public Templates"
                ],
                "parameters": [
                    {
                        "name": "template",
                        "in": "path",
                        "required": true,
                        "description": "UUID шаблона — 36 символов, не число.\n\nБерётся из поля `id`: его возвращает `POST /templates` при создании, а также\n`GET /templates` и `GET /templates/browse` в списках.",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "example": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Шаблон удалён",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`: удалять больше нечего. Факт удаления подтверждает код `200`."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Template deleted",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к шаблону",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Шаблон не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "patch": {
                "operationId": "template.updatePatch",
                "description": "Частичное обновление шаблона: присылайте только те поля, которые меняете. Метод\nпринимает и `PUT`, и `PATCH` — поведение одинаковое.\n\n`config` заменяется **целиком**, а не мержится: чтобы поменять одну настройку,\nпришлите весь объект с ней. `service_key` меняется только вместе с `config` — иначе\nконфиг не пройдёт валидацию по схеме нового сервиса.\n\nИзменение шаблона не затрагивает уже созданные заказы: заказ выполняется, повторяется\n(`POST /orders/{order}/retry`) и показывается (`template.config` в `GET /orders/{order}`)\nс настройками на момент своего создания. Исключение — заказы, созданные до конца\nсентября 2026 года: снимка настроек у них нет, они идут по текущему конфигу шаблона\n(с наложенным `config_override`), и правка шаблона их затрагивает.\n\n**Требуемое право ключа:** `templates.write`.",
                "summary": "Update Template",
                "tags": [
                    "Public Templates"
                ],
                "parameters": [
                    {
                        "name": "template",
                        "in": "path",
                        "required": true,
                        "description": "UUID шаблона — 36 символов, не число.\n\nБерётся из поля `id`: его возвращает `POST /templates` при создании, а также\n`GET /templates` и `GET /templates/browse` в списках.",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "example": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UpdateTemplateRequest",
                                "example": {
                                    "folder_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
                                    "service_key": "string",
                                    "name": "string",
                                    "is_public": true,
                                    "config": []
                                }
                            },
                            "examples": {
                                "rename": {
                                    "summary": "Только переименовать",
                                    "description": "Частичное обновление: передаются лишь изменяемые поля, остальные настройки шаблона сохраняются.",
                                    "value": {
                                        "name": "Диктор — женский, тёплый"
                                    }
                                },
                                "tweak_config": {
                                    "summary": "Подправить настройки голоса",
                                    "description": "config сливается с текущим вглубь — прислать можно только меняемые ключи. service_key при этом обязателен: одно без другого валидация не принимает.",
                                    "value": {
                                        "service_key": "elevenlabs",
                                        "config": {
                                            "tts_settings": {
                                                "voice_settings": {
                                                    "stability": 0.7,
                                                    "speed": 1.1
                                                }
                                            }
                                        }
                                    }
                                },
                                "switch_service": {
                                    "summary": "Сменить сервис",
                                    "description": "Присылайте полный config нового сервиса: старые ключи не исчезают (слияние идёт вглубь), но по схеме нового сервиса они уже ничего не значат.",
                                    "value": {
                                        "service_key": "heygen",
                                        "config": {
                                            "tts_settings": {
                                                "voice_id": "1bd001e7e50f421d891986aad5158bc8",
                                                "speed": 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "description": "Обновление частичное: присылайте только меняемые поля, остальные сохранятся.\n\nДве особенности, из-за которых запрос чаще всего уходит в `422`:\n\n- **`service_key` и `config` ходят парой.** Одно без другого валидация не\n  принимает — даже когда сервис вы не меняете, его код придётся продублировать.\n- **`config` сливается с текущим вглубь, а не заменяет его.** Ключ, который вы\n  не прислали, останется прежним; вернуть его к значению по умолчанию правкой\n  шаблона нельзя — только пересозданием."
                },
                "responses": {
                    "200": {
                        "description": "Шаблон обновлён",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Template",
                                            "description": "Шаблон после обновления — с уже слитым `config`. Сверьтесь с ним, если присылали частичные настройки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Template updated",
                                        "data": {
                                            "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
                                            "user_id": 501,
                                            "slug": null,
                                            "name": "Narrator Voice (EN) v2",
                                            "service_key": "elevenlabs",
                                            "config": {
                                                "tts_settings": {
                                                    "model_id": "eleven_multilingual_v2",
                                                    "voice_id": "21m00Tcm4TlvDq8ikWAM",
                                                    "mode": "mode_v1",
                                                    "language_code": "en",
                                                    "advanced_voice_settings": true,
                                                    "voice_settings": {
                                                        "stability": 0.6,
                                                        "similarity_boost": 0.75,
                                                        "use_speaker_boost": true
                                                    }
                                                }
                                            },
                                            "is_public": false,
                                            "folder_id": null,
                                            "created_at": "2026-06-01T10:00:00+00:00",
                                            "updated_at": "2026-07-17T16:45:00+00:00"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к шаблону",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка валидации",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Errors overview."
                                        },
                                        "errors": {
                                            "type": "object",
                                            "description": "A detailed description of each field that failed validation.",
                                            "additionalProperties": {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    },
                                    "required": [
                                        "message",
                                        "errors"
                                    ],
                                    "example": {
                                        "message": "string",
                                        "errors": {
                                            "key": [
                                                "string"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Шаблон не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/library": {
            "get": {
                "operationId": "voice.library",
                "description": "Личная библиотека: **всё, чем вы можете озвучивать прямо сейчас** — собственные готовые\nголоса плюс добавленные из публичного каталога.\n\nЭто самый практичный список для выбора `voice_id`: в отличие от `GET /voices`, сюда не\nпопадают недоделанные и отклонённые голоса. У каждой записи есть ваше локальное прозвище\n(`nickname`) и признак доступности — фильтруется параметром `available`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "Voice Library",
                "tags": [
                    "Public Voices"
                ],
                "parameters": [
                    {
                        "name": "available",
                        "in": "query",
                        "description": "Только голоса, доступные для генерации прямо сейчас (`true`), либо только недоступные (`false`).",
                        "schema": {
                            "type": "boolean"
                        }
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "description": "Поиск по вашему прозвищу голоса (`nickname`) и его исходному названию.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "language_code",
                        "in": "query",
                        "description": "Фильтр по языку, ISO-639-1 (`ru`, `en`, `de`, …).",
                        "schema": {
                            "type": "string"
                        },
                        "example": "ru"
                    },
                    {
                        "name": "sort_by",
                        "in": "query",
                        "description": "Поле сортировки. По умолчанию `added_at`.",
                        "schema": {
                            "type": "string",
                            "default": "added_at",
                            "enum": [
                                "added_at",
                                "created_at",
                                "nickname"
                            ]
                        }
                    },
                    {
                        "name": "sort_order",
                        "in": "query",
                        "description": "Направление сортировки.",
                        "schema": {
                            "type": "string",
                            "default": "desc",
                            "enum": [
                                "asc",
                                "desc"
                            ]
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "schema": {
                            "type": "integer",
                            "default": 20
                        },
                        "example": 20
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Библиотека голосов владельца ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/UserLibraryList",
                                            "description": "Голоса, добавленные вами в личную библиотеку."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": 12,
                                                    "voice_id": "k3nH9vRt2Lw7ZpXb",
                                                    "nickname": null,
                                                    "added_at": "2026-06-05T10:00:00+00:00",
                                                    "available": true,
                                                    "unavailable_reason": null,
                                                    "origin": "own",
                                                    "voice": {
                                                        "id": "k3nH9vRt2Lw7ZpXb",
                                                        "display_name": "Ava Narrator (EN)",
                                                        "description": "Warm, measured narration voice cloned from a 45-second studio reference — ideal for long-form audiobook and e-learning narration.",
                                                        "gender": "female",
                                                        "accent": "us",
                                                        "default_language_code": "en",
                                                        "voice_status": "ready",
                                                        "publication_status": "public",
                                                        "allow_usage_in_orders": true,
                                                        "preview_generated_at": "2026-06-02T09:15:00+00:00",
                                                        "created_at": "2026-06-01T10:00:00+00:00",
                                                        "updated_at": "2026-06-02T09:15:00+00:00",
                                                        "tags": [
                                                            {
                                                                "id": 8,
                                                                "code": "narration",
                                                                "name": "Narration",
                                                                "description": null,
                                                                "color": null,
                                                                "sort_order": 80
                                                            },
                                                            {
                                                                "id": 4,
                                                                "code": "calm",
                                                                "name": "Calm",
                                                                "description": null,
                                                                "color": null,
                                                                "sort_order": 40
                                                            }
                                                        ],
                                                        "languages": [
                                                            {
                                                                "id": 1,
                                                                "language_code": "en",
                                                                "status": "ready",
                                                                "reference_duration_ms": 47000,
                                                                "model_version": 1,
                                                                "failure_reason": null,
                                                                "clone_started_at": "2026-06-01T10:05:00+00:00",
                                                                "clone_completed_at": "2026-06-01T10:22:00+00:00",
                                                                "updated_at": "2026-06-01T10:22:00+00:00"
                                                            }
                                                        ],
                                                        "preview_urls": {
                                                            "en": "https://s3.lumean.app/lumean/storage/lumvoice/voices/k3nH9vRt2Lw7ZpXb/previews/en.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260602%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260602T091500Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=3f9c2b0a1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a"
                                                        }
                                                    }
                                                },
                                                {
                                                    "id": 13,
                                                    "voice_id": "Xh7Nc2Rt9BqZm4Wv",
                                                    "nickname": "Ad voice backup",
                                                    "added_at": "2026-05-25T14:00:00+00:00",
                                                    "available": false,
                                                    "unavailable_reason": "admin_force_removed",
                                                    "origin": "library",
                                                    "voice": {
                                                        "id": "Xh7Nc2Rt9BqZm4Wv",
                                                        "display_name": "Owen Commercial (EN)",
                                                        "description": "Confident male voice tuned for commercial and advertisement reads.",
                                                        "gender": "male",
                                                        "accent": "uk",
                                                        "default_language_code": "en",
                                                        "voice_status": "ready",
                                                        "publication_status": "rejected",
                                                        "allow_usage_in_orders": false,
                                                        "preview_generated_at": "2026-05-20T12:00:00+00:00",
                                                        "created_at": "2026-05-18T09:00:00+00:00",
                                                        "updated_at": "2026-07-10T11:00:00+00:00",
                                                        "tags": [
                                                            {
                                                                "id": 5,
                                                                "code": "energetic",
                                                                "name": "Energetic",
                                                                "description": null,
                                                                "color": null,
                                                                "sort_order": 50
                                                            }
                                                        ],
                                                        "languages": [
                                                            {
                                                                "id": 3,
                                                                "language_code": "en",
                                                                "status": "ready",
                                                                "reference_duration_ms": 51000,
                                                                "model_version": 1,
                                                                "failure_reason": null,
                                                                "clone_started_at": "2026-05-18T09:05:00+00:00",
                                                                "clone_completed_at": "2026-05-18T09:25:00+00:00",
                                                                "updated_at": "2026-05-18T09:25:00+00:00"
                                                            }
                                                        ],
                                                        "preview_urls": {
                                                            "en": "https://s3.lumean.app/lumean/storage/lumvoice/voices/Xh7Nc2Rt9BqZm4Wv/previews/en.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260520%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260520T120000Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=7a1c9e3f5b2d4a86c0f7e9d1b3a5c8e2f4b6d0a2c4e6f8b1d3a5c7e9f0b2d4a6"
                                                        }
                                                    }
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 20,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/public": {
            "get": {
                "operationId": "voice.publicCatalog",
                "description": "Общий каталог голосов LumVoice, доступных всем пользователям: в выдачу попадают только\nопубликованные (`public`), готовые (`ready`) и разрешённые к заказам голоса.\n\nОтсюда выбирают голос, которого нет в личной библиотеке. Фильтры — по тегам\n(`tag_ids[]`, список — `GET /voices/tags`), языку, полу и поисковой строке.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "Public Voices Catalog",
                "tags": [
                    "Public Voices"
                ],
                "parameters": [
                    {
                        "name": "tag_ids[]",
                        "in": "query",
                        "schema": {
                            "type": "array",
                            "items": {
                                "type": "integer"
                            }
                        },
                        "description": "Фильтр по тегам, множественный: `?tag_ids[]=3&tag_ids[]=7`. Список тегов — `GET /voices/tags`."
                    },
                    {
                        "name": "language_code",
                        "in": "query",
                        "description": "Фильтр по языку, ISO-639-1 (`ru`, `en`, `de`, …).",
                        "schema": {
                            "type": "string"
                        },
                        "example": "ru"
                    },
                    {
                        "name": "gender",
                        "in": "query",
                        "description": "Пол голоса.",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "male",
                                "female",
                                "neutral"
                            ]
                        }
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "description": "Подстрока для поиска. Регистр не учитывается.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "sort",
                        "in": "query",
                        "description": "Порядок выдачи каталога. По умолчанию `popular`.",
                        "schema": {
                            "type": "string",
                            "default": "popular",
                            "enum": [
                                "popular",
                                "newest",
                                "name"
                            ]
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "schema": {
                            "type": "integer",
                            "default": 20
                        },
                        "example": 20
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Публичные голоса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/PublicVoiceList",
                                            "description": "Общий каталог опубликованных голосов. Чтобы пользоваться голосом в заказах, добавлять его в библиотеку не нужно."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": "k3nH9vRt2Lw7ZpXb",
                                                    "display_name": "Ava Narrator (EN)",
                                                    "description": "Warm, measured narration voice cloned from a 45-second studio reference — ideal for long-form audiobook and e-learning narration.",
                                                    "gender": "female",
                                                    "accent": "us",
                                                    "default_language_code": "en",
                                                    "voice_status": "ready",
                                                    "publication_status": "public",
                                                    "allow_usage_in_orders": true,
                                                    "preview_generated_at": "2026-06-02T09:15:00+00:00",
                                                    "created_at": "2026-06-01T10:00:00+00:00",
                                                    "updated_at": "2026-06-02T09:15:00+00:00",
                                                    "tags": [
                                                        {
                                                            "id": 8,
                                                            "code": "narration",
                                                            "name": "Narration",
                                                            "description": null,
                                                            "color": null,
                                                            "sort_order": 80
                                                        },
                                                        {
                                                            "id": 4,
                                                            "code": "calm",
                                                            "name": "Calm",
                                                            "description": null,
                                                            "color": null,
                                                            "sort_order": 40
                                                        }
                                                    ],
                                                    "languages": [
                                                        {
                                                            "id": 1,
                                                            "language_code": "en",
                                                            "status": "ready",
                                                            "reference_duration_ms": 47000,
                                                            "model_version": 1,
                                                            "failure_reason": null,
                                                            "clone_started_at": "2026-06-01T10:05:00+00:00",
                                                            "clone_completed_at": "2026-06-01T10:22:00+00:00",
                                                            "updated_at": "2026-06-01T10:22:00+00:00"
                                                        }
                                                    ],
                                                    "preview_urls": {
                                                        "en": "https://s3.lumean.app/lumean/storage/lumvoice/voices/k3nH9vRt2Lw7ZpXb/previews/en.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260602%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260602T091500Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=3f9c2b0a1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a"
                                                    }
                                                },
                                                {
                                                    "id": "Yt5Fq2Wm8XbNc4Rp",
                                                    "display_name": "Sofia Podcast Host (RU)",
                                                    "description": "Energetic bilingual host voice, cloned for podcast intros and interview segments.",
                                                    "gender": "female",
                                                    "accent": null,
                                                    "default_language_code": "ru",
                                                    "voice_status": "ready",
                                                    "publication_status": "public",
                                                    "allow_usage_in_orders": true,
                                                    "preview_generated_at": "2026-06-20T13:40:00+00:00",
                                                    "created_at": "2026-06-18T08:00:00+00:00",
                                                    "updated_at": "2026-06-20T13:40:00+00:00",
                                                    "tags": [
                                                        {
                                                            "id": 5,
                                                            "code": "energetic",
                                                            "name": "Energetic",
                                                            "description": null,
                                                            "color": null,
                                                            "sort_order": 50
                                                        },
                                                        {
                                                            "id": 2,
                                                            "code": "female",
                                                            "name": "Female",
                                                            "description": null,
                                                            "color": null,
                                                            "sort_order": 20
                                                        }
                                                    ],
                                                    "languages": [
                                                        {
                                                            "id": 4,
                                                            "language_code": "ru",
                                                            "status": "ready",
                                                            "reference_duration_ms": 58000,
                                                            "model_version": 1,
                                                            "failure_reason": null,
                                                            "clone_started_at": "2026-06-18T08:05:00+00:00",
                                                            "clone_completed_at": "2026-06-18T08:30:00+00:00",
                                                            "updated_at": "2026-06-18T08:30:00+00:00"
                                                        }
                                                    ],
                                                    "preview_urls": {
                                                        "ru": "https://s3.lumean.app/lumean/storage/lumvoice/voices/Yt5Fq2Wm8XbNc4Rp/previews/ru.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260620%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260620T134000Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c"
                                                    }
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 20,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/tags": {
            "get": {
                "operationId": "voice.tags",
                "description": "Справочник активных тегов голосов с названиями на языке запроса.\n\nНужен для фильтра `tag_ids[]` в `GET /voices/public`: возьмите `id` подходящих тегов\nотсюда. Язык названий выбирается заголовком `X-Locale`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "Voice Tags",
                "tags": [
                    "Public Voices"
                ],
                "responses": {
                    "200": {
                        "description": "Доступные теги",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/VoiceTag"
                                            },
                                            "description": "Справочник меток целиком — тот же набор, что приходит в `tags` каждого голоса."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": [
                                            {
                                                "id": 1,
                                                "code": "male",
                                                "name": "Male",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 10
                                            },
                                            {
                                                "id": 2,
                                                "code": "female",
                                                "name": "Female",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 20
                                            },
                                            {
                                                "id": 3,
                                                "code": "neutral",
                                                "name": "Neutral",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 30
                                            },
                                            {
                                                "id": 4,
                                                "code": "calm",
                                                "name": "Calm",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 40
                                            },
                                            {
                                                "id": 5,
                                                "code": "energetic",
                                                "name": "Energetic",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 50
                                            },
                                            {
                                                "id": 6,
                                                "code": "young",
                                                "name": "Young",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 60
                                            },
                                            {
                                                "id": 7,
                                                "code": "mature",
                                                "name": "Mature",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 70
                                            },
                                            {
                                                "id": 8,
                                                "code": "narration",
                                                "name": "Narration",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 80
                                            },
                                            {
                                                "id": 9,
                                                "code": "gaming",
                                                "name": "Gaming",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 90
                                            },
                                            {
                                                "id": 10,
                                                "code": "character",
                                                "name": "Character",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 100
                                            },
                                            {
                                                "id": 11,
                                                "code": "warm",
                                                "name": "Warm",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 110
                                            },
                                            {
                                                "id": 12,
                                                "code": "cold",
                                                "name": "Cold",
                                                "description": null,
                                                "color": null,
                                                "sort_order": 120
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices": {
            "get": {
                "operationId": "voice.index",
                "description": "Голоса LumVoice, **созданные владельцем ключа**, в любом статусе — включая те, что ещё\nклонируются или не прошли модерацию.\n\nДля генерации годятся только голоса со `voice_status: ready`; идентификатор такого\nголоса подставляется в `config.tts_settings.voice_id` шаблона или в `config_override`\nзаказа.\n\nПубличный API — read-only: создать или изменить голос по ключу нельзя.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "My Voices",
                "tags": [
                    "Public Voices"
                ],
                "parameters": [
                    {
                        "name": "voice_status",
                        "in": "query",
                        "description": "Стадия жизненного цикла клонированного голоса. Готов к использованию только `ready`.",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "pending_clone",
                                "cloning",
                                "ready",
                                "clone_failed",
                                "archived"
                            ]
                        }
                    },
                    {
                        "name": "publication_status",
                        "in": "query",
                        "description": "Статус публикации голоса в общем каталоге.",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "private",
                                "pending_review",
                                "public",
                                "rejected"
                            ]
                        }
                    },
                    {
                        "name": "language_code",
                        "in": "query",
                        "description": "Фильтр по языку, ISO-639-1 (`ru`, `en`, `de`, …).",
                        "schema": {
                            "type": "string"
                        },
                        "example": "ru"
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "description": "Поиск по названию (`display_name`) и описанию голоса.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "sort_by",
                        "in": "query",
                        "description": "Поле сортировки. По умолчанию `created_at`.",
                        "schema": {
                            "type": "string",
                            "default": "created_at",
                            "enum": [
                                "created_at",
                                "updated_at",
                                "display_name",
                                "last_used_at",
                                "usage_count_total"
                            ]
                        }
                    },
                    {
                        "name": "sort_order",
                        "in": "query",
                        "description": "Направление сортировки.",
                        "schema": {
                            "type": "string",
                            "default": "desc",
                            "enum": [
                                "asc",
                                "desc"
                            ]
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "description": "Сколько записей вернуть на странице. Метаданные пагинации — в `data.meta`.",
                        "schema": {
                            "type": "integer",
                            "default": 20
                        },
                        "example": 20
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Список голосов владельца ключа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/LumVoiceList",
                                            "description": "Ваши голоса LumVoice — и собственные, и добавленные в библиотеку."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "items": [
                                                {
                                                    "id": "Qm4Bx8Yt1WvNc6Zs",
                                                    "display_name": "Marcus Draft (EN)",
                                                    "description": null,
                                                    "gender": "male",
                                                    "accent": null,
                                                    "default_language_code": null,
                                                    "voice_status": "cloning",
                                                    "publication_status": "private",
                                                    "allow_usage_in_orders": true,
                                                    "preview_generated_at": null,
                                                    "created_at": "2026-07-16T18:00:00+00:00",
                                                    "updated_at": "2026-07-17T08:30:00+00:00",
                                                    "tags": [],
                                                    "languages": [
                                                        {
                                                            "id": 2,
                                                            "language_code": "en",
                                                            "status": "cloning",
                                                            "reference_duration_ms": 32000,
                                                            "model_version": 1,
                                                            "failure_reason": null,
                                                            "clone_started_at": "2026-07-17T08:30:00+00:00",
                                                            "clone_completed_at": null,
                                                            "updated_at": "2026-07-17T08:30:00+00:00"
                                                        }
                                                    ],
                                                    "preview_urls": {
                                                        "en": null
                                                    }
                                                },
                                                {
                                                    "id": "k3nH9vRt2Lw7ZpXb",
                                                    "display_name": "Ava Narrator (EN)",
                                                    "description": "Warm, measured narration voice cloned from a 45-second studio reference — ideal for long-form audiobook and e-learning narration.",
                                                    "gender": "female",
                                                    "accent": "us",
                                                    "default_language_code": "en",
                                                    "voice_status": "ready",
                                                    "publication_status": "public",
                                                    "allow_usage_in_orders": true,
                                                    "preview_generated_at": "2026-06-02T09:15:00+00:00",
                                                    "created_at": "2026-06-01T10:00:00+00:00",
                                                    "updated_at": "2026-06-02T09:15:00+00:00",
                                                    "tags": [
                                                        {
                                                            "id": 8,
                                                            "code": "narration",
                                                            "name": "Narration",
                                                            "description": null,
                                                            "color": null,
                                                            "sort_order": 80
                                                        },
                                                        {
                                                            "id": 4,
                                                            "code": "calm",
                                                            "name": "Calm",
                                                            "description": null,
                                                            "color": null,
                                                            "sort_order": 40
                                                        }
                                                    ],
                                                    "languages": [
                                                        {
                                                            "id": 1,
                                                            "language_code": "en",
                                                            "status": "ready",
                                                            "reference_duration_ms": 47000,
                                                            "model_version": 1,
                                                            "failure_reason": null,
                                                            "clone_started_at": "2026-06-01T10:05:00+00:00",
                                                            "clone_completed_at": "2026-06-01T10:22:00+00:00",
                                                            "updated_at": "2026-06-01T10:22:00+00:00"
                                                        }
                                                    ],
                                                    "preview_urls": {
                                                        "en": "https://s3.lumean.app/lumean/storage/lumvoice/voices/k3nH9vRt2Lw7ZpXb/previews/en.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260602%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260602T091500Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=3f9c2b0a1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a"
                                                    }
                                                }
                                            ],
                                            "current_page": 1,
                                            "last_page": 1,
                                            "per_page": 20,
                                            "total": 2
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "У ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationException",
                        "description": "Ошибка валидации. Поля с ошибками перечислены в `errors`."
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/voices/{voice}": {
            "get": {
                "operationId": "voice.show",
                "description": "Карточка одного голоса: язык, пол, теги, статус и превью.\n\nГолос виден, если он ваш, опубликован в общем каталоге либо добавлен в вашу библиотеку.\nЧужой непубличный голос — `403`.\n\n**Требуемое право ключа:** `voices.read`.",
                "summary": "Show Voice",
                "tags": [
                    "Public Voices"
                ],
                "parameters": [
                    {
                        "name": "voice",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "UUID голоса — 36 символов, не число.\n\nБерётся из поля `id` в `GET /voices` (ваши голоса) или `GET /voices/public`\n(общий каталог). Это НЕ `voice_id` внешних провайдеров: у ElevenLabs и HeyGen\nидентификаторы своего формата, и здесь они не подойдут.",
                        "example": "7c0d9e8a-1b23-4a1e-9f3c-2b7d1e605a44"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Детали голоса",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Признак успеха. Решение принимайте по HTTP-коду: у любого ответа 2xx здесь `true`, а ошибка приходит телом другой формы, где этого поля может не быть вовсе."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Пояснение для человека, на языке аккаунта. Показать пользователю можно; разбирать программно — нет: формулировки меняются без предупреждения и кодом ошибки не являются."
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/LumVoice",
                                            "description": "Голос целиком: метки, языковые версии и ссылки на демо-фрагменты."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": true,
                                        "message": "Success",
                                        "data": {
                                            "id": "k3nH9vRt2Lw7ZpXb",
                                            "display_name": "Ava Narrator (EN)",
                                            "description": "Warm, measured narration voice cloned from a 45-second studio reference — ideal for long-form audiobook and e-learning narration.",
                                            "gender": "female",
                                            "accent": "us",
                                            "default_language_code": "en",
                                            "voice_status": "ready",
                                            "publication_status": "public",
                                            "allow_usage_in_orders": true,
                                            "preview_generated_at": "2026-06-02T09:15:00+00:00",
                                            "created_at": "2026-06-01T10:00:00+00:00",
                                            "updated_at": "2026-06-02T09:15:00+00:00",
                                            "tags": [
                                                {
                                                    "id": 8,
                                                    "code": "narration",
                                                    "name": "Narration",
                                                    "description": null,
                                                    "color": null,
                                                    "sort_order": 80
                                                },
                                                {
                                                    "id": 4,
                                                    "code": "calm",
                                                    "name": "Calm",
                                                    "description": null,
                                                    "color": null,
                                                    "sort_order": 40
                                                }
                                            ],
                                            "languages": [
                                                {
                                                    "id": 1,
                                                    "language_code": "en",
                                                    "status": "ready",
                                                    "reference_duration_ms": 47000,
                                                    "model_version": 1,
                                                    "failure_reason": null,
                                                    "clone_started_at": "2026-06-01T10:05:00+00:00",
                                                    "clone_completed_at": "2026-06-01T10:22:00+00:00",
                                                    "updated_at": "2026-06-01T10:22:00+00:00"
                                                }
                                            ],
                                            "preview_urls": {
                                                "en": "https://s3.lumean.app/lumean/storage/lumvoice/voices/k3nH9vRt2Lw7ZpXb/previews/en.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260602%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260602T091500Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=3f9c2b0a1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Нет доступа к голосу или у ключа нет права voices.read",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Error overview."
                                        }
                                    },
                                    "required": [
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "This API key does not have permission to perform this action."
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Невалидный или отсутствующий X-API-KEY",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "data": {
                                            "type": "null",
                                            "description": "Всегда `null`, когда запрос отклонён до обработки."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message",
                                        "data"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "API key is missing. Provide it in the X-API-KEY header.",
                                        "data": null
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Голос не найден",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean",
                                            "description": "Всегда `false` в ответах об ошибке."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Текст ошибки на языке ключа (задаётся заголовком `X-Locale`)."
                                        },
                                        "reason": {
                                            "type": "string",
                                            "description": "Машиночитаемая причина — приходит у бизнес-отказов (например, `invalid_order_status`); у прочих ошибок поля нет. Ветвите обработку по нему, а не по тексту сообщения."
                                        },
                                        "error_code": {
                                            "type": "string",
                                            "description": "Тот же машинный код, что в `reason`, — второе имя поля. Приходит вместе с `reason`, значения всегда совпадают."
                                        }
                                    },
                                    "required": [
                                        "success",
                                        "message"
                                    ],
                                    "example": {
                                        "success": false,
                                        "message": "Resource not found."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/broadcasting/auth": {
            "post": {
                "operationId": "publicBroadcastingAuth",
                "description": "Auth-эндпоинт (Pusher-протокол) для приватных каналов по API-ключу.\n\nВызывается автоматически клиентской библиотекой (Laravel Echo / pusher-js) при подписке\nна `private-orders.{userId}`, `private-order.task.{orderId}`, `private-chunks.preview.{userId}`,\n`private-cartesia-voice-clones.{userId}` или `private-fishaudio-voice-clones.{userId}`. Возвращает auth-токен подписки, который\nбиблиотека использует автоматически.\n\n**Аутентификация:** заголовок `X-API-KEY`. **Право** зависит от канала: каналы заказов и\nпревью чанков — `orders.read`, клоны голоса Cartesia и Fish Audio — `voices.read`.\n\nПодписаться можно только на свои каналы владельца ключа. Подписка на чужой канал → `403`.",
                "summary": "Authorize WebSocket channel subscription (API key)",
                "tags": [
                    "Public Broadcasting"
                ],
                "requestBody": {
                    "description": "Тело в формате протокола Pusher. Клиентская библиотека (Laravel Echo, pusher-js) отправляет его сама при подписке на `private-*` канал — вручную собирать не нужно.",
                    "required": true,
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "socket_id": {
                                        "type": "string",
                                        "description": "Идентификатор WebSocket-соединения, который приходит клиенту в событии `pusher:connection_established`.",
                                        "example": "12345.67890"
                                    },
                                    "channel_name": {
                                        "type": "string",
                                        "description": "Имя канала. По ключу доступны только свои каналы: заказы `private-orders.{userId}` и `private-order.task.{orderId}`, превью чанков `private-chunks.preview.{userId}` (право `orders.read`), клоны голоса Cartesia `private-cartesia-voice-clones.{userId}` и Fish Audio `private-fishaudio-voice-clones.{userId}` (право `voices.read`), подарочные коды реселлера `private-giftcodes.{userId}` (право `reseller.read`).",
                                        "example": "private-orders.42"
                                    }
                                },
                                "required": [
                                    "socket_id",
                                    "channel_name"
                                ],
                                "example": {
                                    "socket_id": "12345.67890",
                                    "channel_name": "private-orders.42"
                                }
                            },
                            "examples": {
                                "order_channel": {
                                    "summary": "Подписка на канал заказа",
                                    "description": "Тело собирает клиентская библиотека (Laravel Echo, pusher-js) — руками его слать не нужно. Пример показывает, что именно уйдёт на сервер.",
                                    "value": {
                                        "socket_id": "12345.67890",
                                        "channel_name": "private-order.task.9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c"
                                    }
                                },
                                "orders_channel": {
                                    "summary": "Подписка на все свои заказы",
                                    "description": "Канал со статусами всех заказов владельца ключа: номер в имени канала — ID пользователя, чужой не подпишется.",
                                    "value": {
                                        "socket_id": "12345.67890",
                                        "channel_name": "private-orders.42"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Подписка авторизована. Клиент получает auth-токен подписки.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "auth": {
                                            "type": "string",
                                            "description": "Подпись подписки на канал. Верните её клиенту Pusher/Echo как есть — он подставит её сам. Своего конверта у этого ответа нет: форму диктует протокол.",
                                            "example": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
                                        }
                                    },
                                    "required": [
                                        "auth"
                                    ],
                                    "example": {
                                        "auth": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Отсутствует/невалидный `X-API-KEY` (заголовок)."
                    },
                    "403": {
                        "description": "У ключа нет права, которого требует канал (`orders.read` — заказы и превью чанков, `voices.read` — клоны голоса Cartesia и Fish Audio, `reseller.read` — коды реселлера), либо запрошен чужой канал; чужой канал → 403."
                    },
                    "422": {
                        "description": "Невалидные `socket_id`/`channel_name` (например, `channel_name` без префикса `private-`)."
                    },
                    "429": {
                        "description": "Превышен rate-limit — 1200 запросов в минуту."
                    }
                },
                "security": [
                    {
                        "X-API-KEY": []
                    }
                ]
            }
        }
    },
    "components": {
        "securitySchemes": {
            "X-API-KEY": {
                "type": "apiKey",
                "in": "header",
                "name": "X-API-KEY"
            }
        },
        "schemas": {
            "AiGatewayCatalogModel": {
                "type": "object",
                "properties": {
                    "key": {
                        "type": "string",
                        "description": "То самое имя, которое надо ставить в поле `model` запроса. Оно же\nединственное принимаемое: гейтвей резолвит его точным совпадением\nи уже сам подставляет имя модели на стороне поставщика."
                    },
                    "name": {
                        "type": "string"
                    },
                    "family": {
                        "type": "string"
                    },
                    "kind": {
                        "description": "`chat` | `image`. Модель `image` зовут через `/v1/images` (или\nчатом — ответ придёт markdown-картинкой), цена — `price.request`.",
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "string",
                                "enum": [
                                    "chat"
                                ]
                            }
                        ]
                    },
                    "description": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "context_window": {
                        "type": "string"
                    },
                    "max_output_tokens": {
                        "type": "string"
                    },
                    "capabilities": {
                        "type": "string"
                    },
                    "pricing_mode": {
                        "type": "string"
                    },
                    "price": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "description": "`null` — отпускной нет: ни одна пара не продаёт модель не в\nминус. Нули здесь обещали бы бесплатную модель.",
                        "additionalProperties": {
                            "type": "object",
                            "properties": {
                                "per_mtok_minor": {
                                    "type": "integer"
                                },
                                "per_mtok_formatted": {
                                    "type": [
                                        "number",
                                        "null"
                                    ]
                                }
                            },
                            "required": [
                                "per_mtok_minor",
                                "per_mtok_formatted"
                            ]
                        }
                    },
                    "billing_available": {
                        "type": "boolean",
                        "description": "Вызываема ли модель платным ключом. `false` при живой модели —\nона видна ради своего ключа (BYOK), а платный вызов откажет."
                    },
                    "byok": {
                        "type": "string"
                    }
                },
                "required": [
                    "key",
                    "name",
                    "family",
                    "kind",
                    "description",
                    "context_window",
                    "max_output_tokens",
                    "capabilities",
                    "pricing_mode",
                    "price",
                    "billing_available",
                    "byok"
                ],
                "title": "AiGatewayCatalogModel"
            },
            "Asset": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Внутренний идентификатор актива."
                    },
                    "code": {
                        "type": "string",
                        "description": "Код актива: `LUMC` — внутренняя валюта, `TOKEN` — токены генерации."
                    },
                    "kind": {
                        "$ref": "#/components/schemas/AssetKind",
                        "description": "Вид актива: деньги или расходуемые единицы. От него зависит, где сумма считается платежом, а где — расходом квоты."
                    },
                    "precision": {
                        "type": "integer",
                        "description": "Число десятичных знаков. **Ключевое поле:** любая целая сумма этого актива делится на `10^precision`, чтобы получить реальное значение. Не считайте, что делитель равен 100."
                    },
                    "name": {
                        "type": "string",
                        "description": "Название актива для показа, на языке аккаунта."
                    },
                    "deleted_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда актив выведен из обращения, ISO 8601. `null` у действующего."
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда актив заведён, ISO 8601."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда актив последний раз менялся, ISO 8601."
                    }
                },
                "required": [
                    "id",
                    "code",
                    "kind",
                    "precision",
                    "name",
                    "deleted_at",
                    "created_at",
                    "updated_at"
                ],
                "title": "Asset"
            },
            "BrowseTemplates": {
                "type": "object",
                "properties": {
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/TemplateItem"
                        },
                        "description": "Содержимое текущей папки — вперемешку папки и шаблоны, в порядке сортировки. Различать их можно по наличию `service_key`: он есть только у шаблона."
                    },
                    "current_page": {
                        "type": "integer",
                        "description": "Номер текущей страницы, с 1."
                    },
                    "last_page": {
                        "type": "integer",
                        "description": "Номер последней страницы. Равен `current_page` — значит, дальше ничего нет."
                    },
                    "per_page": {
                        "type": "integer",
                        "description": "Сколько записей на странице."
                    },
                    "total": {
                        "type": "integer",
                        "description": "Сколько записей в папке всего, по всем страницам."
                    },
                    "current_folder": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "properties": {
                            "id": {
                                "type": "string",
                                "description": "UUID текущей папки — его подставляют в `folder_id`, чтобы открыть её снова."
                            },
                            "name": {
                                "type": "string",
                                "description": "Название текущей папки."
                            }
                        },
                        "required": [
                            "id",
                            "name"
                        ],
                        "description": "Папка, содержимое которой показано. `null` — вы в корне."
                    },
                    "breadcrumbs": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "id": {
                                    "type": "string",
                                    "format": "uuid",
                                    "description": "UUID папки-предка — значение для `folder_id`, чтобы подняться на этот уровень."
                                },
                                "name": {
                                    "type": "string",
                                    "description": "Название папки-предка."
                                }
                            }
                        },
                        "description": "Путь от корня до текущей папки, от предка к потомку — готов к отрисовке навигационной цепочки."
                    }
                },
                "required": [
                    "items",
                    "current_page",
                    "last_page",
                    "per_page",
                    "total",
                    "current_folder",
                    "breadcrumbs"
                ],
                "title": "BrowseTemplates"
            },
            "CartesiaVoiceClone": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "UUID клона. Его подставляют в пути `/voices/cartesia/clones/{voiceClone}/…`; в шаблон он НЕ идёт — туда идёт `voice_id`."
                    },
                    "provider": {
                        "type": "string",
                        "const": "cartesia",
                        "description": "Провайдер клона — всегда `cartesia`."
                    },
                    "display_name": {
                        "type": "string",
                        "description": "Название клона, заданное при создании."
                    },
                    "language": {
                        "type": "string",
                        "description": "Язык образца (ISO 639-1) либо `null`, если не передавался."
                    },
                    "description": {
                        "type": "string",
                        "description": "Ваше описание клона либо `null`."
                    },
                    "status": {
                        "type": "string",
                        "description": "Статус: `pending` → `cloning` → `ready` либо `failed`; `archived` — снят владельцем; `banned` — заблокирован модератором. Озвучивать можно только `ready`."
                    },
                    "voice_id": {
                        "type": "string",
                        "description": "Идентификатор голоса Cartesia — значение для `tts_settings.voice_id` шаблона с `service_key: cartesia`. Есть только у `ready`, иначе `null`."
                    },
                    "attempts": {
                        "type": "integer",
                        "description": "Номер попытки клонирования: 1 при создании, +1 за каждый повтор."
                    },
                    "error": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "properties": {
                            "reason": {
                                "type": "string",
                                "description": "Машинный код отказа: `processing_error`, `max_retries`, `network`, `timeout`, `dispatch_failed`. Повторить попытку — `POST /voices/cartesia/clones/{voiceClone}/retry`; если `processing_error` повторяется, смените образец."
                            },
                            "message": {
                                "type": "string",
                                "description": "Пояснение к отказу для человека либо `null`. Ветвитесь по `reason`, а не по тексту."
                            }
                        },
                        "required": [
                            "reason",
                            "message"
                        ],
                        "description": "Причина отказа последней попытки; `null`, пока отказа не было."
                    },
                    "created_at": {
                        "type": "string",
                        "description": "Когда клон создан."
                    },
                    "completed_at": {
                        "type": "string",
                        "description": "Когда клон стал `ready`; `null` до этого."
                    },
                    "failed_at": {
                        "type": "string",
                        "description": "Когда последняя попытка завершилась отказом; `null`, если отказа нет."
                    },
                    "banned_at": {
                        "type": "string",
                        "description": "Когда клон заблокирован модератором (`status = banned`); `null`, если блокировки нет."
                    },
                    "ban_reason": {
                        "type": "string",
                        "description": "Причина блокировки от модератора либо `null`. Заблокированный клон нельзя ни использовать, ни архивировать."
                    }
                },
                "required": [
                    "id",
                    "provider",
                    "display_name",
                    "language",
                    "description",
                    "status",
                    "voice_id",
                    "attempts",
                    "error",
                    "created_at",
                    "completed_at",
                    "failed_at",
                    "banned_at",
                    "ban_reason"
                ],
                "title": "CartesiaVoiceClone"
            },
            "CartesiaVoiceCloneStatus": {
                "type": "string",
                "description": "Статус клона голоса Cartesia: `pending` — в очереди, `cloning` — клонируется,\n`ready` — готов, есть `voice_id`; `failed` — отказ (причина в `error`), можно\nповторить; `archived` — снят владельцем; `banned` — заблокирован модератором\n(причина в `ban_reason`).",
                "enum": [
                    "pending",
                    "cloning",
                    "ready",
                    "failed",
                    "archived",
                    "banned"
                ],
                "title": "CartesiaVoiceCloneStatus"
            },
            "CartesiaVoice": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "Внутренний id записи каталога. Для озвучки не нужен — в шаблон идёт `voice_id`."
                    },
                    "voice_id": {
                        "type": "string",
                        "description": "Идентификатор голоса Cartesia — значение для `tts_settings.voice_id` шаблона с `service_key: cartesia`."
                    },
                    "name": {
                        "type": "string",
                        "description": "Название голоса."
                    },
                    "description": {
                        "type": "string",
                        "description": "Описание голоса от Cartesia либо `null`."
                    },
                    "language": {
                        "type": "string",
                        "description": "Язык голоса (ISO 639-1) либо `null`."
                    },
                    "gender": {
                        "type": "string",
                        "description": "Пол голоса: `masculine`, `feminine`, `gender_neutral` либо `null`."
                    },
                    "preview_url": {
                        "type": "string",
                        "description": "Ссылка на демо-запись голоса либо `null`."
                    }
                },
                "required": [
                    "id",
                    "voice_id",
                    "name",
                    "description",
                    "language",
                    "gender",
                    "preview_url"
                ],
                "title": "CartesiaVoice"
            },
            "CreateCartesiaVoiceCloneRequest": {
                "type": "object",
                "description": "Новый клон голоса Cartesia: образец записи в base64 и согласие на использование\nголоса. Принимается как `application/json`.",
                "properties": {
                    "display_name": {
                        "type": "string",
                        "maxLength": 128,
                        "description": "Название клона — видно только вам, до 128 символов.",
                        "example": "Мой голос"
                    },
                    "reference_audio": {
                        "type": "string",
                        "description": "Образец голоса в base64; data URI (`data:audio/wav;base64,…`) тоже подходит.\nДо 20 МБ после декодирования; формат — обычная аудиозапись (WAV, MP3 и т. п.).\nНужна чистая речь одного человека без музыки и шума.",
                        "example": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA="
                    },
                    "language": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "enum": [
                            "en",
                            "ru",
                            "uk",
                            "de",
                            "fr",
                            "es",
                            "pt",
                            "it",
                            "nl",
                            "pl",
                            "sv",
                            "tr",
                            "cs",
                            "sk",
                            "bg",
                            "ro",
                            "hu",
                            "el",
                            "fi",
                            "da",
                            "no",
                            "hr",
                            "ka",
                            "he",
                            "ar",
                            "hi",
                            "bn",
                            "ta",
                            "te",
                            "gu",
                            "kn",
                            "ml",
                            "mr",
                            "pa",
                            "ja",
                            "ko",
                            "zh",
                            "vi",
                            "th",
                            "id",
                            "ms",
                            "tl"
                        ],
                        "description": "Язык образца, код ISO 639-1 из списка. Не передан — язык по умолчанию воркера.",
                        "example": "ru"
                    },
                    "description": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "maxLength": 500,
                        "description": "Ваше описание клона, до 500 символов."
                    },
                    "consent_biometric": {
                        "type": "boolean",
                        "description": "Согласие на обработку биометрических данных — образца голоса (https://lumean.app/consent/biometric). Пишется в журнал согласий вместе с id клона. `true` — согласие дано.",
                        "example": true
                    },
                    "voice_owner": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "enum": [
                            "self",
                            "third_party"
                        ],
                        "description": "Чей голос в образце: `self` — ваш, `third_party` — чужой, и у вас есть согласие его владельца.",
                        "example": "self"
                    },
                    "consent_confirmed": {
                        "type": "string",
                        "description": "Прежняя галочка «это ваш голос или у вас есть право его использовать». Нужна, только если не передан `consent_biometric: true`; когда согласия станут обязательными, вместо неё — `consent_biometric` и `voice_owner`.",
                        "example": true
                    }
                },
                "required": [
                    "display_name",
                    "reference_audio",
                    "consent_confirmed"
                ],
                "title": "CreateCartesiaVoiceCloneRequest"
            },
            "CreateFishAudioVoiceCloneRequest": {
                "type": "object",
                "description": "Новый клон голоса Fish Audio: образец записи в base64 и согласие на использование\nголоса. Принимается как `application/json`.",
                "properties": {
                    "display_name": {
                        "type": "string",
                        "maxLength": 128,
                        "description": "Название клона — видно только вам, до 128 символов.",
                        "example": "Мой голос"
                    },
                    "reference_audio": {
                        "type": "string",
                        "description": "Образец голоса в base64; data URI (`data:audio/wav;base64,…`) тоже подходит.\nДо 20 МБ после декодирования; формат — обычная аудиозапись (WAV, MP3 и т. п.).\nНужна чистая речь одного человека без музыки и шума.",
                        "example": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA="
                    },
                    "language": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "pattern": "^[a-z]{2}$",
                        "description": "Справочная метка клона, двухбуквенный код ISO 639-1 (`ru`, `en`, …). У голоса Fish Audio своего языка нет, провайдеру метка не передаётся.",
                        "example": "ru"
                    },
                    "description": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "maxLength": 500,
                        "description": "Ваше описание клона, до 500 символов."
                    },
                    "consent_biometric": {
                        "type": "boolean",
                        "description": "Согласие на обработку биометрических данных — образца голоса (https://lumean.app/consent/biometric). Пишется в журнал согласий вместе с id клона. `true` — согласие дано.",
                        "example": true
                    },
                    "voice_owner": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "enum": [
                            "self",
                            "third_party"
                        ],
                        "description": "Чей голос в образце: `self` — ваш, `third_party` — чужой, и у вас есть согласие его владельца.",
                        "example": "self"
                    },
                    "consent_confirmed": {
                        "type": "string",
                        "description": "Прежняя галочка «это ваш голос или у вас есть право его использовать». Нужна, только если не передан `consent_biometric: true`; когда согласия станут обязательными, вместо неё — `consent_biometric` и `voice_owner`.",
                        "example": true
                    }
                },
                "required": [
                    "display_name",
                    "reference_audio",
                    "consent_confirmed"
                ],
                "title": "CreateFishAudioVoiceCloneRequest"
            },
            "Entitlement": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Внутренний идентификатор возможности."
                    },
                    "code": {
                        "type": "string",
                        "description": "Код возможности — по нему сервис проверяет, открыт ли он вам."
                    },
                    "name": {
                        "type": "string",
                        "description": "Название возможности для показа, на языке аккаунта."
                    },
                    "metadata": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "additionalProperties": true,
                        "description": "Дополнительные атрибуты возможности. Набор ключей произвольный."
                    },
                    "is_active": {
                        "type": "boolean",
                        "description": "Действует ли возможность."
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда возможность заведена, ISO 8601."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда возможность последний раз менялась, ISO 8601."
                    },
                    "is_unlimited": {
                        "type": "boolean",
                        "description": "Безлимитна ли эта возможность в рамках тарифа. Поле присутствует только когда право отдаётся в контексте плана."
                    }
                },
                "required": [
                    "id",
                    "code",
                    "name",
                    "metadata",
                    "is_active",
                    "created_at",
                    "updated_at"
                ],
                "title": "Entitlement"
            },
            "FishAudioVoiceClone": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "UUID клона. Его подставляют в пути `/voices/fishaudio/clones/{voiceClone}/…`; в шаблон он НЕ идёт — туда идёт `voice_id`."
                    },
                    "provider": {
                        "type": "string",
                        "const": "fishaudio",
                        "description": "Провайдер клона — всегда `fishaudio`."
                    },
                    "display_name": {
                        "type": "string",
                        "description": "Название клона, заданное при создании."
                    },
                    "language": {
                        "type": "string",
                        "description": "Справочная метка языка (ISO 639-1) либо `null`, если не передавалась. На клонирование не влияет."
                    },
                    "description": {
                        "type": "string",
                        "description": "Ваше описание клона либо `null`."
                    },
                    "status": {
                        "type": "string",
                        "description": "Статус: `pending` → `cloning` → `ready` либо `failed`; `archived` — снят владельцем; `banned` — заблокирован модератором. Озвучивать можно только `ready`."
                    },
                    "voice_id": {
                        "type": "string",
                        "description": "Идентификатор голоса Fish Audio (строка из 32 hex-символов, не UUID) — значение для `tts_settings.voice_id` шаблона с `service_key: fishaudio`. Есть только у `ready`, иначе `null`."
                    },
                    "attempts": {
                        "type": "integer",
                        "description": "Номер попытки клонирования: 1 при создании, +1 за каждый повтор."
                    },
                    "error": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "properties": {
                            "reason": {
                                "type": "string",
                                "description": "Машинный код отказа: `processing_error`, `max_retries`, `network`, `timeout`, `dispatch_failed`, `unbanned` (клон разбанен, но голоса у него не было). Повторить попытку — `POST /voices/fishaudio/clones/{voiceClone}/retry`; если `processing_error` повторяется, смените образец."
                            },
                            "message": {
                                "type": "string",
                                "description": "Пояснение к отказу для человека либо `null`. Ветвитесь по `reason`, а не по тексту."
                            }
                        },
                        "required": [
                            "reason",
                            "message"
                        ],
                        "description": "Причина отказа последней попытки; `null`, пока отказа не было."
                    },
                    "created_at": {
                        "type": "string",
                        "description": "Когда клон создан."
                    },
                    "completed_at": {
                        "type": "string",
                        "description": "Когда клон стал `ready`; `null` до этого."
                    },
                    "failed_at": {
                        "type": "string",
                        "description": "Когда последняя попытка завершилась отказом; `null`, если отказа нет."
                    },
                    "banned_at": {
                        "type": "string",
                        "description": "Когда клон заблокирован модератором (`status = banned`); `null`, если блокировки нет."
                    },
                    "ban_reason": {
                        "type": "string",
                        "description": "Причина блокировки от модератора либо `null`. Заблокированный клон нельзя ни использовать, ни архивировать."
                    }
                },
                "required": [
                    "id",
                    "provider",
                    "display_name",
                    "language",
                    "description",
                    "status",
                    "voice_id",
                    "attempts",
                    "error",
                    "created_at",
                    "completed_at",
                    "failed_at",
                    "banned_at",
                    "ban_reason"
                ],
                "title": "FishAudioVoiceClone"
            },
            "FishAudioVoiceCloneStatus": {
                "type": "string",
                "description": "Статус клона голоса Fish Audio: `pending` — в очереди, `cloning` — клонируется,\n`ready` — готов, есть `voice_id`; `failed` — отказ (причина в `error`), можно\nповторить; `archived` — снят владельцем; `banned` — заблокирован модератором\n(причина в `ban_reason`).",
                "enum": [
                    "pending",
                    "cloning",
                    "ready",
                    "failed",
                    "archived",
                    "banned"
                ],
                "title": "FishAudioVoiceCloneStatus"
            },
            "FishAudioVoice": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "Внутренний id записи каталога. Для озвучки не нужен — в шаблон идёт `voice_id`."
                    },
                    "voice_id": {
                        "type": "string",
                        "description": "Идентификатор голоса Fish Audio (строка из 32 hex-символов, не UUID) — значение для `tts_settings.voice_id` шаблона с `service_key: fishaudio`."
                    },
                    "name": {
                        "type": "string",
                        "description": "Название голоса."
                    },
                    "description": {
                        "type": "string",
                        "description": "Описание голоса от Fish Audio либо `null`."
                    },
                    "language": {
                        "type": "string",
                        "description": "Язык выгрузки каталога (`ru`, `en`, `es`) либо `null`."
                    },
                    "languages": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                },
                                "minItems": 0,
                                "maxItems": 0,
                                "additionalItems": false
                            }
                        ],
                        "description": "Все языки голоса по данным Fish Audio."
                    },
                    "gender": {
                        "type": "string",
                        "description": "Пол голоса: `male`, `female` либо `null`."
                    },
                    "tags": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                },
                                "minItems": 0,
                                "maxItems": 0,
                                "additionalItems": false
                            }
                        ],
                        "description": "Теги голоса у Fish Audio."
                    },
                    "author_name": {
                        "type": "string",
                        "description": "Автор голоса у Fish Audio либо `null`."
                    },
                    "task_count": {
                        "type": "integer",
                        "description": "Сколько раз голос использовали у Fish Audio — мера популярности; каталог отсортирован по ней."
                    },
                    "like_count": {
                        "type": "integer",
                        "description": "Число лайков голоса у Fish Audio."
                    },
                    "preview_url": {
                        "type": "string",
                        "description": "Временная ссылка (60 минут) на MP3-сэмпл голоса либо `null`, пока превью не скачано."
                    }
                },
                "required": [
                    "id",
                    "voice_id",
                    "name",
                    "description",
                    "language",
                    "languages",
                    "gender",
                    "tags",
                    "author_name",
                    "task_count",
                    "like_count",
                    "preview_url"
                ],
                "title": "FishAudioVoice"
            },
            "GetFileUrlRequest": {
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "Путь файла целиком, как он пришёл в `result.files[]` или `result.service_files[]`\nзаказа. Собирать путь вручную не нужно и не получится — он содержит служебные\nсегменты.",
                        "example": "storage/42/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/result/output.mp3"
                    },
                    "download": {
                        "type": "boolean",
                        "description": "При `true` ссылка помечается как attachment: файл скачается под человекочитаемым именем заказа, а не откроется во вкладке браузера.",
                        "default": false
                    },
                    "number_base": {
                        "type": "integer",
                        "description": "С какого числа нумеруются чанки в имени файла: `1` → первый чанк «001», `0` → «000». По умолчанию `1`.",
                        "enum": [
                            "0",
                            "1"
                        ],
                        "default": 1
                    },
                    "number_padding": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 4,
                        "description": "Ширина номера чанка с ведущими нулями, `1..4`: `3` → «007». По умолчанию `3`. Держите одинаковым с запросом ZIP-архива, иначе один и тот же чанк называется по-разному поштучно и внутри архива.",
                        "default": 3
                    }
                },
                "required": [
                    "path"
                ],
                "title": "GetFileUrlRequest",
                "description": "Запрос временной ссылки на файл результата. Ссылку качайте обычным `GET`,\nбез заголовка `X-API-KEY`."
            },
            "GiftCodeBatch": {
                "type": "object",
                "properties": {
                    "uuid": {
                        "type": "string",
                        "description": "Идентификатор партии."
                    },
                    "source": {
                        "type": "string",
                        "description": "Всегда `reseller` — партия выпущена с баланса реселлера."
                    },
                    "codes_count": {
                        "type": "string",
                        "description": "Сколько кодов в партии."
                    },
                    "nominal_total": {
                        "type": "string",
                        "description": "Сумма номиналов кодов, миноры LMC."
                    },
                    "nominal_total_formatted": {
                        "type": "number",
                        "description": "Сумма номиналов, LMC."
                    },
                    "cost_total": {
                        "type": "string",
                        "description": "Сколько списано с баланса реселлера (номиналы минус скидка), миноры LMC."
                    },
                    "cost_total_formatted": {
                        "type": "number",
                        "description": "Списано с баланса реселлера, LMC."
                    },
                    "discount_bps": {
                        "type": "integer",
                        "description": "Личная скидка реселлера на момент выпуска, базисные пункты (1000 = 10%)."
                    },
                    "codes": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/GiftCode"
                        },
                        "description": "Коды партии с ПОЛНЫМ значением `code` — сохраните их: это деньги на предъявителя."
                    },
                    "created_at": {
                        "type": "string",
                        "description": "Когда партия выпущена."
                    },
                    "currency_asset": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string",
                                "const": "LUMC",
                                "description": "Код актива, в котором выражены суммы."
                            },
                            "precision": {
                                "type": "integer",
                                "description": "Сколько знаков после запятой у LMC: `real = value / 10^precision`."
                            }
                        },
                        "required": [
                            "code",
                            "precision"
                        ],
                        "description": "Валюта сумм ответа — LMC. По `precision` суммы в минимальных единицах переводятся в целые LMC."
                    }
                },
                "required": [
                    "uuid",
                    "source",
                    "codes_count",
                    "nominal_total",
                    "nominal_total_formatted",
                    "created_at",
                    "currency_asset"
                ],
                "title": "GiftCodeBatch"
            },
            "GiftCodeList": {
                "title": "GiftCodeList",
                "type": "object",
                "properties": {
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/GiftCode"
                        },
                        "description": "Записи текущей страницы в порядке сортировки списка."
                    },
                    "current_page": {
                        "type": "integer",
                        "description": "Номер текущей страницы, с единицы."
                    },
                    "last_page": {
                        "type": "integer",
                        "description": "Номер последней страницы. Обход списка закончен, когда `current_page` равен `last_page`."
                    },
                    "per_page": {
                        "type": "integer",
                        "description": "Размер страницы, применённый сервером."
                    },
                    "total": {
                        "type": "integer",
                        "description": "Сколько записей всего по фильтру."
                    }
                },
                "required": [
                    "items",
                    "current_page",
                    "last_page",
                    "per_page",
                    "total"
                ]
            },
            "GiftCode": {
                "type": "object",
                "properties": {
                    "uuid": {
                        "type": "string",
                        "description": "UUID кода. Его подставляют в пути `GET /reseller/codes/{uuid}` и `POST /reseller/codes/{uuid}/cancel`."
                    },
                    "code_masked": {
                        "type": "string",
                        "description": "Код с маской, `LUM-****-****-XXXX`: его безопасно показывать в интерфейсе и писать в логи."
                    },
                    "code": {
                        "type": "string",
                        "description": "ПОЛНОЕ значение кода — то, что вводит получатель при активации. Публичный API реселлера отдаёт его в списке, при показе и при выпуске. Обращайтесь как с секретом: кто знает код, тот его и активирует."
                    },
                    "nominal": {
                        "type": "string",
                        "description": "Номинал кода в минимальных единицах LMC: `real = nominal / 10^currency_asset.precision`. Столько LMC получит активатор."
                    },
                    "nominal_formatted": {
                        "type": "number",
                        "description": "Тот же номинал в целых LMC — число, готовое к показу."
                    },
                    "cost": {
                        "type": "string",
                        "description": "Сколько код стоил реселлеру с учётом скидки, в минимальных единицах LMC. Приходит только у кодов реселлера (`source: reseller`)."
                    },
                    "cost_formatted": {
                        "type": "number",
                        "description": "Та же стоимость в целых LMC. Приходит только у кодов реселлера."
                    },
                    "discount_bps": {
                        "type": "integer",
                        "description": "Скидка реселлера на момент выпуска, в базисных пунктах (`100` = 1%). Позднейшая смена личной скидки этот код не меняет. Приходит только у кодов реселлера."
                    },
                    "source": {
                        "type": "string"
                    },
                    "funding": {
                        "type": "string"
                    },
                    "status": {
                        "type": "string"
                    },
                    "activatable_at": {
                        "type": "string",
                        "description": "С какого момента код можно активировать: до него код заморожен. `null` — ограничения нет."
                    },
                    "redeemed_at": {
                        "type": "string",
                        "description": "Когда код активирован. `null`, пока не активирован. Кто его активировал, API не раскрывает."
                    },
                    "cancelled_at": {
                        "type": "string",
                        "description": "Когда реселлер отменил код. `null`, если код не отменялся."
                    },
                    "created_at": {
                        "type": "string",
                        "description": "Когда код выпущен."
                    },
                    "currency_asset": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string",
                                "const": "LUMC",
                                "description": "Код актива, в котором выражены суммы."
                            },
                            "precision": {
                                "type": "integer",
                                "description": "Сколько знаков после запятой у LMC: `real = value / 10^precision`."
                            }
                        },
                        "required": [
                            "code",
                            "precision"
                        ],
                        "description": "Валюта сумм ответа — LMC. По `precision` суммы в минимальных единицах переводятся в целые LMC."
                    }
                },
                "required": [
                    "uuid",
                    "code_masked",
                    "nominal",
                    "nominal_formatted",
                    "source",
                    "funding",
                    "status",
                    "activatable_at",
                    "redeemed_at",
                    "cancelled_at",
                    "created_at",
                    "currency_asset"
                ],
                "title": "GiftCode"
            },
            "GiftCodeSource": {
                "type": "string",
                "description": "Откуда код: куплен пользователем за деньги (`purchase`) или выпущен реселлером\nсо своего баланса (`reseller`).",
                "enum": [
                    "purchase",
                    "reseller"
                ],
                "title": "GiftCodeSource"
            },
            "GiftCodeStatus": {
                "type": "string",
                "description": "Статус подарочного кода: `active` — выпущен и ждёт активации (возможно, ещё\nзаморожен до `activatable_at`); `refunding` — идёт возврат денег, активация и\nотмена недоступны; `redeemed` — активирован, номинал на балансе активатора;\n`refunded` — деньги возвращены покупателю на карту; `cancelled` — реселлер\nотменил код, стоимость вернулась на его баланс; `annulled` — аннулирован без\nвозврата денег.",
                "enum": [
                    "active",
                    "refunding",
                    "redeemed",
                    "refunded",
                    "cancelled",
                    "annulled"
                ],
                "title": "GiftCodeStatus"
            },
            "IssueGiftCodesRequest": {
                "type": "object",
                "description": "Выпуск кодов с баланса реселлера. Заголовок `Idempotency-Key` необязателен,\nно рекомендуется: с ним сетевой повтор не выпустит вторую партию.",
                "properties": {
                    "nominals": {
                        "type": "array",
                        "items": {
                            "type": "number"
                        },
                        "minItems": 1,
                        "maxItems": 1000,
                        "description": "Номиналы кодов в LMC, по одному числу на код: сколько кодов — столько элементов. Допустимы числа и десятичные строки; номинал — из готовых в `GET /reseller/settings` либо между `min_nominal` и `max_nominal`. Число элементов ограничено `limits.max_codes_per_issue` из `GET /reseller`.",
                        "example": [
                            100,
                            100,
                            250
                        ]
                    }
                },
                "required": [
                    "nominals"
                ],
                "title": "IssueGiftCodesRequest"
            },
            "LumVoiceList": {
                "title": "LumVoiceList",
                "type": "object",
                "properties": {
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/LumVoice"
                        },
                        "description": "Записи текущей страницы в порядке сортировки списка."
                    },
                    "current_page": {
                        "type": "integer",
                        "description": "Номер текущей страницы, с единицы."
                    },
                    "last_page": {
                        "type": "integer",
                        "description": "Номер последней страницы. Обход списка закончен, когда `current_page` равен `last_page`."
                    },
                    "per_page": {
                        "type": "integer",
                        "description": "Размер страницы, применённый сервером."
                    },
                    "total": {
                        "type": "integer",
                        "description": "Сколько записей всего по фильтру."
                    }
                },
                "required": [
                    "items",
                    "current_page",
                    "last_page",
                    "per_page",
                    "total"
                ]
            },
            "LumVoice": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "UUID голоса. Кладётся в `config.voice_id` шаблона; для путей `/voices/{voice}` — он же."
                    },
                    "display_name": {
                        "type": "string",
                        "description": "Название голоса для показа."
                    },
                    "description": {
                        "type": "string",
                        "description": "Описание голоса от автора: тембр, манера, сценарии использования."
                    },
                    "gender": {
                        "type": "string",
                        "description": "Пол голоса."
                    },
                    "accent": {
                        "type": "string",
                        "description": "Акцент или региональный вариант произношения."
                    },
                    "default_language_code": {
                        "type": "string",
                        "description": "Язык, на котором голос звучит по умолчанию, ISO-639-1."
                    },
                    "voice_status": {
                        "type": "string",
                        "description": "Состояние клонирования голоса. Использовать в заказах можно только полностью готовый."
                    },
                    "publication_status": {
                        "type": "string",
                        "description": "Стадия публикации: черновик, на модерации, опубликован. Определяет, виден ли голос в общем каталоге `GET /voices/public`."
                    },
                    "allow_usage_in_orders": {
                        "type": "boolean",
                        "description": "Разрешил ли автор использовать голос другим. Без этого флага голос не попадает в каталог `GET /voices/public`, а у тех, кто уже добавил его в свою библиотеку, запись становится недоступной."
                    },
                    "preview_generated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда собраны демо-фрагменты, ISO 8601. `null` — превью ещё нет."
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда голос создан, ISO 8601."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда голос последний раз менялся, ISO 8601."
                    },
                    "tags": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/VoiceTag"
                        },
                        "description": "Метки голоса для поиска и фильтрации. Полный справочник — `GET /voices/tags`."
                    },
                    "languages": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/VoiceLanguage"
                        },
                        "description": "Языки, на которые голос склонирован, с состоянием каждого клона. Заказ возможен только на готовом языке."
                    },
                    "preview_urls": {
                        "type": "object",
                        "additionalProperties": {
                            "type": [
                                "string",
                                "null"
                            ]
                        },
                        "description": "Демо-фрагменты по языкам: ключ — код языка, значение — временная ссылка на аудио либо `null`, если превью для языка нет. Ссылки живут около минуты."
                    }
                },
                "required": [
                    "id",
                    "display_name",
                    "description",
                    "gender",
                    "accent",
                    "default_language_code",
                    "voice_status",
                    "publication_status",
                    "allow_usage_in_orders",
                    "preview_generated_at",
                    "created_at",
                    "updated_at",
                    "preview_urls"
                ],
                "title": "LumVoice"
            },
            "OrderList": {
                "title": "OrderList",
                "type": "object",
                "properties": {
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Order"
                        },
                        "description": "Записи текущей страницы в порядке сортировки списка."
                    },
                    "current_page": {
                        "type": "integer",
                        "description": "Номер текущей страницы, с единицы."
                    },
                    "last_page": {
                        "type": "integer",
                        "description": "Номер последней страницы. Обход списка закончен, когда `current_page` равен `last_page`."
                    },
                    "per_page": {
                        "type": "integer",
                        "description": "Размер страницы, применённый сервером."
                    },
                    "total": {
                        "type": "integer",
                        "description": "Сколько записей всего по фильтру."
                    }
                },
                "required": [
                    "items",
                    "current_page",
                    "last_page",
                    "per_page",
                    "total"
                ]
            },
            "OrderItem": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "UUID чанка. Подставляется в пути `/orders/{order}/items/{item}/…`."
                    },
                    "order_id": {
                        "type": "string",
                        "description": "UUID заказа, которому принадлежит чанк."
                    },
                    "item_type": {
                        "type": "string",
                        "description": "Природа записи: `chunk` — исходный фрагмент, `retry` — бесплатный повтор\nупавшего фрагмента, `regeneration` — платная переделка готового.\n\nПовторы и перегенерации живут отдельными записями с тем же `chunk_index`:\nистория попыток сохраняется целиком, а звучит в результате последняя."
                    },
                    "chunk_index": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Порядковый номер фрагмента в заказе, с нуля. Задаёт порядок склейки итогового файла."
                    },
                    "paragraph_index": {
                        "type": "string",
                        "description": "Номер абзаца, к которому относится фрагмент, — заполняется только у исходных фрагментов абзацного заказа. `null` и в обычном режиме, и у записей повтора либо перегенерации: у них своей привязки к абзацу нет, её берут у родительского фрагмента через `parent_item_id`. Режим включается ключом `generation_mode` в `config_override` заказа."
                    },
                    "parent_item_id": {
                        "type": "string",
                        "description": "UUID исходного чанка — у записей `retry` и `regeneration`. У самого исходного чанка `null`."
                    },
                    "original_text_length": {
                        "type": "integer",
                        "description": "Длина текста, с которой фрагмент создавался. Служит потолком для `text` в `regenerate`."
                    },
                    "current_text_length": {
                        "type": "integer",
                        "description": "Длина актуального текста фрагмента: после перегенерации отличается от `original_text_length`."
                    },
                    "price_units": {
                        "type": "integer",
                        "description": "Стоимость фрагмента в **токенах** — это не деньги. Денежная сумма лежит у самого заказа, в его поле `price_units` (там LMC, здесь токены; поля называются одинаково, единицы разные)."
                    },
                    "consumed_units": {
                        "type": "integer",
                        "description": "Сколько токенов фактически списано по этому фрагменту. У незавершённого фрагмента — 0."
                    },
                    "remaining_locked": {
                        "type": "integer",
                        "description": "Сколько токенов ещё зарезервировано под фрагмент. Обнуляется, когда фрагмент завершается или отменяется."
                    },
                    "status": {
                        "type": "string",
                        "description": "Состояние фрагмента: `pending` — ждёт воркера, `processing` — в работе,\n`completed` — готов, `failed` — техническая ошибка, `policy_flagged` —\nотклонён контентной политикой.\n\nРазница между двумя последними определяет способ лечения: `failed` чинится\nповтором как есть, `policy_flagged` — только переформулировкой текста,\nповтор с прежним будет отклонён снова."
                    },
                    "result_file": {
                        "anyOf": [
                            {
                                "type": "object",
                                "additionalProperties": {}
                            },
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "description": "Файл готового фрагмента: абсолютный путь либо структура с путём и размером. Скачивается через `POST /storage/url`, как и файлы заказа. `null`, пока фрагмент не готов."
                    },
                    "duration_ms": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Длительность полученного аудио в миллисекундах — длина записи, а не время генерации. У не-аудио задач `null`."
                    },
                    "attempt_number": {
                        "type": "integer",
                        "description": "Номер попытки для этого фрагмента, начиная с 1. Растёт с каждым повтором."
                    },
                    "can_retry": {
                        "type": "boolean",
                        "description": "Разрешён ли бесплатный повтор прямо сейчас. Учитывает статус, тип задачи и исчерпание попыток — проверять условия самому не нужно."
                    },
                    "can_regenerate": {
                        "type": "boolean",
                        "description": "Разрешена ли платная перегенерация прямо сейчас. Доступна только для завершённых фрагментов."
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда фрагмент создан, ISO 8601."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда фрагмент последний раз менялся, ISO 8601."
                    },
                    "failure_reason": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Причина падения в машиночитаемом виде — есть только у `failed` и `policy_flagged`. Остальным приходит `null`."
                    }
                },
                "required": [
                    "id",
                    "order_id",
                    "item_type",
                    "chunk_index",
                    "paragraph_index",
                    "parent_item_id",
                    "original_text_length",
                    "current_text_length",
                    "price_units",
                    "consumed_units",
                    "remaining_locked",
                    "status",
                    "result_file",
                    "duration_ms",
                    "attempt_number",
                    "can_retry",
                    "can_regenerate",
                    "created_at",
                    "updated_at"
                ],
                "title": "OrderItem"
            },
            "Order": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "UUID заказа. Его подставляют в пути `/orders/{order}/…` и передают в `POST /orders/{order}/retry`."
                    },
                    "user_id": {
                        "type": "integer",
                        "description": "ID владельца заказа. У заказов, созданных по API-ключу, это всегда владелец ключа — чужой заказ по ключу не увидеть."
                    },
                    "name": {
                        "type": "string",
                        "description": "Название заказа: из него берутся имя скачиваемого файла и имя ZIP-архива."
                    },
                    "template": {
                        "$ref": "#/components/schemas/Template",
                        "description": "Шаблон, по которому создан заказ. Приходит только у заказов с `template_id`; у заказа без шаблона (`sfx`/`music` по `task_type`) поля нет. `config` внутри — настройки, с которыми выполняется ИМЕННО этот заказ: конфиг шаблона на момент создания заказа с наложенным `config_override`; правка шаблона после создания его не меняет. В списке `GET /orders` `config` приходит `null` — полный конфиг отдаёт `GET /orders/{order}`. Шаблон могли удалить после создания заказа: здесь он виден по-прежнему, но `GET /templates/{template}` ответит `404`, а новый заказ с этим `template_id` — `422`. У заказов, созданных до выхода этой версии API (конец сентября 2026 года), снимка нет: там `config` — текущий конфиг шаблона с наложенным `config_override`, и правка шаблона его меняет."
                    },
                    "template_id": {
                        "type": "string",
                        "description": "UUID использованного шаблона либо `null`, если заказ создан без шаблона."
                    },
                    "task_type": {
                        "$ref": "#/components/schemas/TaskType",
                        "description": "Тип задачи — он определяет, что лежит в `task_data` и что придёт в `result`."
                    },
                    "status": {
                        "$ref": "#/components/schemas/OrderStatus",
                        "description": "Текущее состояние заказа. Терминальные — `completed`, `failed`, `canceled`,\n`compensated`; опрашивать заказ дальше смысла нет.\n\n`partially_completed` — часть чанков готова, часть упала: файлы уже можно\nзабирать, а сбойные фрагменты перезапускать через\n`POST /orders/{order}/items/retry-failed`."
                    },
                    "price_units": {
                        "description": "Заблокированная под заказ сумма в минимальных единицах LMC. Показывать это число как есть нельзя: реальное значение — `price_units / 10^precision`, где `precision` берётся из актива рядом. Готовое к показу значение лежит в соседнем decimal-поле.",
                        "type": "integer"
                    },
                    "price_formatted": {
                        "type": [
                            "number",
                            "null"
                        ],
                        "description": "Та же сумма, что и `price_units`, но в целых LMC — готова к показу. `null`, пока стоимость не зафиксирована."
                    },
                    "currency_asset": {
                        "description": "Актив суммы — всегда LMC. Его `precision` нужен, чтобы перевести минимальные единицы в реальное значение.",
                        "$ref": "#/components/schemas/Asset"
                    },
                    "total_tokens": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Полная стоимость заказа в токенах: исходные чанки плюс все повторы и перегенерации. Это единица квоты, а не деньги."
                    },
                    "total_duration_ms": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Суммарная длительность полученного аудио в миллисекундах. Это длина результата, а не время, потраченное на генерацию."
                    },
                    "task_data": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "description": "Входные данные задачи ровно в том виде, в каком их принял сервер: текст, ссылки на загруженные файлы, применённые настройки. Набор ключей зависит от `task_type`."
                    },
                    "result": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "description": "Результат выполнения. `files[]` — основные файлы; `service_files[]` —\nвспомогательные (субтитры `.srt`/`.vtt`/`.lrc`, alignment) у TTS-заказов.\nПути **абсолютные** и готовы к передаче в `POST /storage/url`.\n\n`null`, пока заказ не завершён, а также у упавшего заказа: подробностей\nошибки в публичном контуре нет."
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда заказ создан, ISO 8601."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда заказ последний раз менялся, ISO 8601. Растёт на каждом переходе статуса — годится как дешёвый признак «что-то произошло»."
                    },
                    "files_expire_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Срок хранения файлов заказа. `files_expire_at` — момент удаления (`null`, если заказ ещё не завершён или файлы уже удалены); `files_expired` — файлов больше нет, скачивать нечего."
                    },
                    "files_expired": {
                        "type": "boolean",
                        "description": "`true` — срок хранения истёк и файлы уже удалены: ссылку на них получить нельзя, заказ нужно создавать заново."
                    },
                    "retention": {
                        "type": "object",
                        "description": "Сроки хранения по уровням файлов. Пофрагментные дорожки склеенного заказа удаляются раньше самого результата.",
                        "properties": {
                            "chunks": {
                                "type": "object",
                                "properties": {
                                    "expires_at": {
                                        "type": "string",
                                        "description": "Когда исчезнут пофрагментные дорожки, ISO 8601. До этого момента доступны правка пауз, пересборка и перегенерация отдельного фрагмента. `null` — заказ ещё не завершён (отсчёт не начат) либо фрагментов уже нет."
                                    },
                                    "expired": {
                                        "type": "boolean",
                                        "description": "`true` — пофрагментных дорожек больше нет: правка и пересборка отклоняются. Готовый результат при этом может быть ещё доступен — смотрите `retention.files`."
                                    },
                                    "expires_early": {
                                        "type": "boolean",
                                        "description": "`true` — фрагменты исчезнут раньше результата (заказ склеен в отдельный файл). `false` — фрагменты и есть результат, оба уровня истекают одновременно."
                                    },
                                    "reason": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "enum": [
                                            "chunks_purged",
                                            "storage_expired",
                                            null
                                        ],
                                        "description": "Почему фрагменты недоступны: `chunks_purged` — удалены по своему сроку, результат ещё на месте; `storage_expired` — удалены все файлы заказа. `null` — фрагменты доступны."
                                    }
                                },
                                "required": [
                                    "expires_at",
                                    "expired",
                                    "expires_early",
                                    "reason"
                                ],
                                "description": "Срок жизни пофрагментных дорожек — ими работают правка пауз, пересборка финала и перегенерация отдельного фрагмента."
                            },
                            "files": {
                                "type": "object",
                                "properties": {
                                    "expires_at": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "description": "Когда будут удалены все файлы заказа, ISO 8601. То же значение, что в `files_expire_at` — поле верхнего уровня сохранено для совместимости."
                                    },
                                    "expired": {
                                        "type": "boolean",
                                        "description": "То же, что `files_expired`: файлов больше нет, скачивать нечего."
                                    }
                                },
                                "required": [
                                    "expires_at",
                                    "expired"
                                ],
                                "description": "Срок жизни всех файлов заказа, включая результат. Дублирует поля `files_expire_at` / `files_expired` верхнего уровня."
                            }
                        },
                        "required": [
                            "chunks",
                            "files"
                        ]
                    },
                    "eta_seconds": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Оценка оставшегося времени, в секундах — только пока заказ выполняется. У завершённого и у ещё не начатого `null`."
                    },
                    "eta_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Ожидаемый момент готовности, ISO 8601 — тот же прогноз, что и `eta_seconds`, только абсолютным временем."
                    },
                    "completed_chunks": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Сколько исходных чанков уже готово. Повторы и перегенерации сюда не входят — иначе прогресс уезжал бы выше 100 %."
                    },
                    "total_chunks": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Сколько исходных чанков в заказе всего. Ноль означает, что разбиение ещё не выполнено."
                    },
                    "progress_percent": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Готовность в процентах, 0–100 — производная от `completed_chunks` и `total_chunks`, считать самому не нужно."
                    },
                    "stitching": {
                        "type": "boolean",
                        "description": "`true` — идёт склейка аудио: результат ещё не финальный. Бывает и при `status = completed` — после перегенерации фрагмента финал пересобирается, и до конца склейки `result` указывает на прежнюю дорожку. Скачивать итог — когда `stitching = false`."
                    },
                    "paragraphs": {
                        "type": [
                            "array",
                            "null"
                        ],
                        "description": "Сводка по абзацам: индекс, число фрагментов, общий статус и прогресс каждого абзаца. `null` — заказ создан в обычном режиме, ориентируйтесь на `items[]`. Чтобы абзацы появились, создавайте заказ с `config_override.generation_mode = paragraph` (подробности — в описании этого поля).",
                        "items": {
                            "type": "object",
                            "properties": {
                                "paragraph_index": {
                                    "type": "integer",
                                    "description": "Номер абзаца, с нуля."
                                },
                                "chunks_count": {
                                    "anyOf": [
                                        {
                                            "type": "integer"
                                        },
                                        {
                                            "type": "number"
                                        }
                                    ],
                                    "description": "Сколько чанков попало в этот абзац."
                                },
                                "completed_chunks": {
                                    "anyOf": [
                                        {
                                            "type": "string"
                                        },
                                        {
                                            "type": "integer",
                                            "enum": [
                                                0
                                            ]
                                        }
                                    ],
                                    "description": "Сколько чанков абзаца уже готово."
                                },
                                "status": {
                                    "type": "string",
                                    "description": "Сводный статус абзаца: он выводится из статусов его чанков, а не хранится отдельно. Словарь свой, короче общего: `pending`, `in_progress`, `failed`, `completed` — заблокированный модерацией чанк тоже даёт `failed`. Считаются только исходные чанки, поэтому успешный повтор упавшего фрагмента статус абзаца НЕ меняет: смотрите на сам фрагмент в `items[]`."
                                },
                                "progress_percent": {
                                    "type": "integer",
                                    "description": "Готовность абзаца в процентах, 0–100."
                                },
                                "result_file": {
                                    "type": [
                                        "string",
                                        "null"
                                    ],
                                    "description": "Склеенное аудио абзаца целиком: абсолютный путь либо структура с путём и размером. Скачивается через `POST /storage/url`, как и остальные файлы заказа. `null` читается по-разному в зависимости от `chunks_count`: при `chunks_count = 1` собственного файла у абзаца нет и не будет — абзац это и есть тот единственный фрагмент, берите его `result_file` из `items[]` по тому же `paragraph_index`; при `chunks_count > 1` склейки ещё не было — многочанковый абзац получает файл ТОЛЬКО после неё, никогда заранее."
                                }
                            },
                            "required": [
                                "paragraph_index",
                                "chunks_count",
                                "completed_chunks",
                                "status",
                                "progress_percent",
                                "result_file"
                            ]
                        }
                    },
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/OrderItem"
                        },
                        "description": "Чанки заказа. Приходят в детальном ответе `GET /orders/{order}`; в списке `GET /orders` поля нет — за ними идите в `GET /orders/{order}/items`."
                    },
                    "tokens_from_quota": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Сколько из потраченных токенов покрыла квота подписки. `null` — из квоты не покрыто ничего."
                    },
                    "tokens_from_payg": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Сколько токенов оплачено сверх квоты, из баланса LMC. `null` — доплаты не было."
                    },
                    "tokens_spent": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Итог по двум предыдущим полям: сколько токенов списано за заказ (квота + доплата). Заказ, покрытый безлимитом подписки, и бесплатные версии сюда не входят — у безлимитного заказа поле `null`; объём генерации — `total_tokens`. Повторы (`retry`) не входят — они не тарифицируются заново."
                    },
                    "queue_position": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Место в очереди — только у задач поиска голоса (`voice_search`), которые обслуживаются по одной. У остальных типов задач поля нет."
                    },
                    "queue_wait_seconds": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Оценка ожидания в очереди поиска голоса, в секундах. Оценка, а не обещание: пересчитывается на каждый ответ."
                    }
                },
                "required": [
                    "id",
                    "user_id",
                    "name",
                    "template_id",
                    "task_type",
                    "status",
                    "price_units",
                    "price_formatted",
                    "currency_asset",
                    "total_tokens",
                    "total_duration_ms",
                    "task_data",
                    "result",
                    "created_at",
                    "updated_at",
                    "files_expire_at",
                    "files_expired",
                    "retention",
                    "eta_seconds",
                    "eta_at",
                    "completed_chunks",
                    "total_chunks",
                    "progress_percent",
                    "stitching",
                    "paragraphs"
                ],
                "title": "Order"
            },
            "OrderStatus": {
                "type": "string",
                "enum": [
                    "created",
                    "pending",
                    "in_progress",
                    "completed",
                    "result_delivered",
                    "failed",
                    "compensated",
                    "cancelled",
                    "partially_completed"
                ],
                "title": "OrderStatus"
            },
            "PreviewChunksRequest": {
                "type": "object",
                "description": "Dry-run preview разбиения текста на чанки — без создания заказа.\ninput_text обязателен (preview по inline-тексту; файлы — async-путь).\n\n`config_override` — тот же объект, что уйдёт в `POST /orders` (с 25.09.2026): размер\nчанка и режим провайдера в нём меняют рез, и превью без него показывало границы\nконфига шаблона, а не боевого реза ({@see AcceptsConfigOverride}).",
                "properties": {
                    "template_id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "task_type": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "input_text": {
                        "type": "string"
                    },
                    "previous_preview_id": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "ID предыдущего preview для инкрементального ре-чанка (пересчёт только хвоста правки)."
                    },
                    "text_hash": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "sha256(input_text) от фронта — необязательная серверная верификация целостности.",
                        "pattern": "^[a-f0-9]{64}$"
                    },
                    "generation_mode": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Режим генерации ЭТОГО превью — то же значение, что уйдёт в\n`config_override.generation_mode` при создании заказа. Отсутствие = `default`.\nСервис, не объявивший ключ в `validation_schema`, режим игнорирует\n(см. Service::resolveGenerationMode) — как и на боевом пути.",
                        "enum": [
                            "default",
                            "paragraph"
                        ]
                    },
                    "config_override": {
                        "type": "object",
                        "description": "Настройки генерации поверх конфига шаблона — тот же JSON-объект, что уйдёт в создание заказа; настройки, влияющие на рез, применяются к превью так же, как к заказу. Посторонние ключи отбрасываются."
                    }
                },
                "required": [
                    "template_id",
                    "input_text"
                ],
                "title": "PreviewChunksRequest"
            },
            "PublicVoiceList": {
                "title": "PublicVoiceList",
                "type": "object",
                "properties": {
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/LumVoice"
                        },
                        "description": "Записи текущей страницы в порядке сортировки списка."
                    },
                    "current_page": {
                        "type": "integer",
                        "description": "Номер текущей страницы, с единицы."
                    },
                    "last_page": {
                        "type": "integer",
                        "description": "Номер последней страницы. Обход списка закончен, когда `current_page` равен `last_page`."
                    },
                    "per_page": {
                        "type": "integer",
                        "description": "Размер страницы, применённый сервером."
                    },
                    "total": {
                        "type": "integer",
                        "description": "Сколько записей всего по фильтру."
                    }
                },
                "required": [
                    "items",
                    "current_page",
                    "last_page",
                    "per_page",
                    "total"
                ]
            },
            "RegenerateOrderItemRequest": {
                "type": "object",
                "description": "Параметры перегенерации готового фрагмента заказа.",
                "properties": {
                    "text": {
                        "type": "string",
                        "maxLength": 50000,
                        "description": "Новый текст готового чанка — **обязателен**. Переделка уже выполненного фрагмента\nтарифицируется как новая генерация (проверка токен-квоты ключа → `429` при\nпревышении).",
                        "example": "Новая редакция фрагмента."
                    }
                },
                "required": [
                    "text"
                ],
                "title": "RegenerateOrderItemRequest"
            },
            "ResellerProfile": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "ID реселлера."
                    },
                    "status": {
                        "type": "string"
                    },
                    "discount_bps": {
                        "type": "string",
                        "description": "Личная скидка реселлера на выпускаемые коды, в базисных пунктах: `100` = 1%. Именно она определяет стоимость кода: `cost = nominal × (1 − discount_bps / 10000)`."
                    },
                    "blocked_reason": {
                        "type": "string",
                        "description": "Причина блокировки кабинета. `null`, пока кабинет не заблокирован."
                    },
                    "approved_at": {
                        "type": "string",
                        "description": "Когда заявка на роль реселлера была одобрена."
                    },
                    "limits": {
                        "type": "string",
                        "description": "Лимиты — личные, если администратор их задал, иначе общие умолчания: `max_topup_rub` — потолок одного пополнения в рублях, `max_codes_per_issue` — сколько кодов можно выпустить за один запрос `POST /reseller/codes`."
                    },
                    "balance": {
                        "type": "object",
                        "additionalProperties": {
                            "anyOf": [
                                {
                                    "type": "integer"
                                },
                                {
                                    "type": "number"
                                }
                            ]
                        },
                        "description": "Баланс кабинета. Каждая сумма приходит парой: в минимальных единицах LMC (`available`, `held`, `debt`, …; `real = value / 10^currency_asset.precision`) и `<имя>_formatted` — то же в целых LMC. `available` — на счёте, `held` — заморожено, `debt` — долг перед платформой (его гасит следующее пополнение), `spendable` — сколько реально можно потратить на выпуск (`available` за вычетом долга)."
                    },
                    "currency_asset": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string",
                                "const": "LUMC",
                                "description": "Код актива, в котором выражены суммы."
                            },
                            "precision": {
                                "type": "integer",
                                "description": "Сколько знаков после запятой у LMC: `real = value / 10^precision`."
                            }
                        },
                        "required": [
                            "code",
                            "precision"
                        ],
                        "description": "Валюта сумм ответа — LMC. По `precision` суммы в минимальных единицах переводятся в целые LMC."
                    }
                },
                "required": [
                    "id",
                    "status",
                    "discount_bps",
                    "blocked_reason",
                    "approved_at",
                    "currency_asset"
                ],
                "title": "ResellerProfile"
            },
            "RetryOrderItemRequest": {
                "type": "object",
                "description": "Параметры повтора сбойного фрагмента заказа.",
                "properties": {
                    "text": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "maxLength": 50000,
                        "description": "Исправленный текст чанка. Не передан — чанк повторяется с прежним текстом.\n\nДля чанка в статусе `policy_flagged` (контент отклонён политикой провайдера)\n**повтор с тем же текстом будет отклонён снова** — присылайте переформулированный.\nТекущий текст чанка: `GET /orders/{order}/items/{item}/text`.",
                        "example": "Переформулированный фрагмент текста."
                    }
                },
                "title": "RetryOrderItemRequest"
            },
            "RetryOrderRequest": {
                "type": "object",
                "description": "Подтверждение биллинга для повторного заказа. Пустое тело — обычный путь;\nполя нужны только после ответа `402` в режиме отката к явному подтверждению.",
                "properties": {
                    "confirm_payg_topup": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "Согласие на PAYG-добор для нового заказа — только в режиме отката к явному подтверждению (иначе игнорируется). Семантика та же, что у `POST /orders`: действует только вместе со свежим `quote_token` из ответа `402`.",
                        "default": false
                    },
                    "quote_token": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Котировка добора из ответа `402` этого же запроса (только режим отката). Живёт ~60 секунд."
                    }
                },
                "title": "RetryOrderRequest"
            },
            "Service": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Внутренний числовой идентификатор сервиса. В запросах используйте `code`, а не его."
                    },
                    "code": {
                        "type": "string",
                        "description": "Код сервиса — то самое значение, которое идёт в `service_key` шаблона и в путь `GET /usage/{service}`."
                    },
                    "name": {
                        "type": "string",
                        "description": "Служебное название сервиса."
                    },
                    "display_name": {
                        "type": "string",
                        "description": "Название сервиса для показа пользователю, переведённое на язык аккаунта."
                    },
                    "label": {
                        "anyOf": [
                            {
                                "type": "null"
                            },
                            {
                                "type": "array",
                                "items": {}
                            },
                            {
                                "type": "string"
                            },
                            {
                                "type": "object"
                            }
                        ]
                    },
                    "payg_access": {
                        "type": "string",
                        "description": "Разрешён ли доплатный (pay-as-you-go) добор сверх квоты подписки для этого сервиса."
                    },
                    "billing_mode": {
                        "$ref": "#/components/schemas/BillingMode",
                        "description": "Модель тарификации сервиса: за вызов, за символ, за секунду либо за изображение. Она определяет, какое из полей `units_per_*` работает."
                    },
                    "unit_asset_id": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "ID актива, в котором номинированы единицы сервиса. Сам актив — в `unit_asset`."
                    },
                    "unit_asset": {
                        "$ref": "#/components/schemas/Asset",
                        "description": "Актив единиц сервиса; его `precision` нужен, чтобы перевести целые `units_*` в дробное значение."
                    },
                    "required_entitlement_id": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "ID возможности, без которой сервис недоступен. `null` — сервис доступен всем."
                    },
                    "required_entitlement": {
                        "$ref": "#/components/schemas/Entitlement",
                        "description": "Возможность подписки, открывающая доступ к сервису. Есть ли она у вас — видно в `GET /usage`."
                    },
                    "units_per_call": {
                        "description": "Значение в минимальных единицах актива: реальное — `value / 10^precision`, где `precision` лежит в `unit_asset`.",
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "units_per_char": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Сколько единиц стоит один символ — работает при посимвольной тарификации. Значение в минимальных единицах актива."
                    },
                    "units_per_image": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Сколько единиц стоит одно изображение. Значение в минимальных единицах актива."
                    },
                    "units_per_second": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Сколько единиц стоит секунда результата. Значение в минимальных единицах актива."
                    },
                    "min_billable_chars": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Минимум символов, который тарифицируется, даже если текст короче. Защищает от копеечных заказов."
                    },
                    "lumcoin_per_token": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Цена одного токена при доплате из LMC, в минимальных единицах. Стоимость добора — `tokens × lumcoin_per_token`."
                    },
                    "lumc_precision": {
                        "type": "integer",
                        "description": "Число десятичных знаков LMC — делитель `10^precision` для всех сумм в минимальных единицах."
                    },
                    "price_lumc_minor": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Цена в минимальных единицах LMC за `unit_size` натуральных единиц сервиса. `null` — сервис тарифицируется через токены."
                    },
                    "unit_size": {
                        "type": "integer",
                        "description": "За сколько натуральных единиц сервиса назначена цена `price_lumc_minor`: например, за 1000 символов."
                    },
                    "uses_money_first_pricing": {
                        "type": "boolean",
                        "description": "`true` — цена задана сразу в LMC (`price_lumc_minor`), а не выводится из токенов. Влияет только на способ расчёта, не на итог для вас."
                    },
                    "rate_unit": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Натуральная единица тарифа для показа: символы, секунды, изображения."
                    },
                    "display_category": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Раздел каталога, к которому сервис отнесён в интерфейсе."
                    },
                    "is_premium": {
                        "type": "boolean",
                        "description": "Помечен ли сервис как премиальный. Признак для витрины; на доступ влияет `required_entitlement`."
                    },
                    "display_order": {
                        "type": "integer",
                        "description": "Вес сортировки в каталоге: меньше — выше."
                    },
                    "metadata": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "additionalProperties": true,
                        "description": "Дополнительные атрибуты сервиса. Набор ключей произвольный и может меняться — не полагайтесь на конкретный."
                    },
                    "is_active": {
                        "type": "boolean",
                        "description": "Виден ли сервис в каталоге. Приём заказов останавливает не он, а maintenance_mode."
                    },
                    "maintenance_mode": {
                        "type": "boolean",
                        "description": "Сервис на технических работах: создание заказа, повтор и перегенерация отвечают 503. Уже принятые заказы продолжают обрабатываться."
                    },
                    "access_level": {
                        "type": "string"
                    },
                    "maintenance_auto_resume": {
                        "type": "boolean"
                    },
                    "maintenance_started_at": {
                        "type": "string"
                    },
                    "maintenance_note": {
                        "type": "string"
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда сервис заведён, ISO 8601."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда настройки сервиса менялись, ISO 8601."
                    }
                },
                "required": [
                    "id",
                    "code",
                    "name",
                    "display_name",
                    "payg_access",
                    "billing_mode",
                    "unit_asset_id",
                    "required_entitlement_id",
                    "units_per_call",
                    "units_per_char",
                    "units_per_image",
                    "units_per_second",
                    "min_billable_chars",
                    "lumcoin_per_token",
                    "lumc_precision",
                    "price_lumc_minor",
                    "unit_size",
                    "uses_money_first_pricing",
                    "rate_unit",
                    "display_category",
                    "is_premium",
                    "display_order",
                    "metadata",
                    "is_active",
                    "maintenance_mode",
                    "created_at",
                    "updated_at"
                ],
                "title": "Service"
            },
            "StoreOrderRequest": {
                "type": "object",
                "description": "Параметры создания заказа. Принимается как `application/json`, так и\n`multipart/form-data` (второе — когда нужны `input_files`).",
                "properties": {
                    "template_id": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "UUID шаблона — он определяет сервис, тип задачи и все настройки генерации.\n**Либо `template_id`, либо `task_type` обязателен**; если переданы оба, ведущим\nсчитается шаблон.\n\nШаблон — обычный способ работы: настройки заданы один раз, в заказе остаётся\nприслать только текст. Разовое отклонение от настроек шаблона — `config_override`.",
                        "example": "4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c"
                    },
                    "task_type": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "enum": [
                            "tts",
                            "image",
                            "txt2img",
                            "remix",
                            "image_edit",
                            "voice_clone",
                            "sfx",
                            "music",
                            "voice_search"
                        ],
                        "description": "Тип задачи для генерации **без шаблона**. Обязателен, если не передан `template_id`.\n\n| Значение | Что делает | Где параметры |\n| --- | --- | --- |\n| `sfx` | Звуковой эффект по текстовому описанию | `task_data` |\n| `music` | Музыкальный трек по описанию | `task_data` |\n| `voice_search` | Подбор похожих голосов по вашему аудиофрагменту | `input_files` (аудио) + `task_data.top` |\n\nДля `tts` шаблон нужен — в нём лежит выбор голоса и модели.",
                        "example": "sfx"
                    },
                    "name": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Человекочитаемое имя заказа: определяет имя скачиваемого файла и имя ZIP-архива.\n\nНе передано — сервер выведет сам в таком порядке: имя загруженного файла без\nрасширения → первые ~60 символов текста по границе слова (паузные теги `{{pause=…}}`\nвырезаются).",
                        "maxLength": 120,
                        "example": "Приветствие для лендинга"
                    },
                    "task_data": {
                        "type": "object",
                        "description": "Параметры генерации для заказа **без шаблона** — «временный шаблон» на один заказ.\nJSON-объект; multipart-клиент может прислать его одной JSON-строкой.\n\n> **Ключи кладите в корень объекта, без обёртки.** `GET /templates/config-options`\n> показывает те же параметры сгруппированными в секцию (`sfx_settings`,\n> `music_settings`) — это форма для интерфейса настроек, а не форма запроса.\n> Прислав `{\"sfx_settings\": {\"text\": \"…\"}}`, вы получите `422` «text обязателен»:\n> обёртка спрятала ключ.\n\n**SFX** (`task_type: \"sfx\"`) — одна генерация = один файл:\n\n| Ключ | Тип | Дефолт | Назначение |\n| --- | --- | --- | --- |\n| `text` | string, **обязателен** | — | Описание звука; английский промпт работает лучше |\n| `prompt_influence` | number `0..1` | `0.3` | Насколько строго держаться промпта |\n| `duration_seconds` | number `0.5..30` | `null` | `null` = авто-длительность |\n| `loop` | boolean | `false` | Бесшовное зацикливание |\n| `output_format` | `mp3_44100_128` \\| `mp3_44100_192` \\| `opus_48000_128` | `mp3_44100_128` | Формат результата |\n\n**Music** (`task_type: \"music\"`) — `n_variants` генераций = столько же файлов:\n\n| Ключ | Тип | Дефолт | Назначение |\n| --- | --- | --- | --- |\n| `prompt` | string, **обязателен** | — | Описание трека: жанр, настроение, инструменты |\n| `n_variants` | integer `1..4` | `1` | Сколько вариантов сгенерировать. **Умножает цену** и даёт столько же файлов в `result.files[]` |\n| `lyrics_text` | string | `null` | Текст песни; пусто и без `force_instrumental` — модель может добавить вокал сама |\n| `force_instrumental` | boolean | `false` | Инструментал без вокала |\n| `music_length_ms` | integer `10000..30000` | `30000` | Длительность в **миллисекундах**: 10–**30 сек максимум**, больше `30000` → 422 |\n| `model_id` | `music_v2` | `music_v2` | Пока единственное значение |\n\n**Voice Search** (`task_type: \"voice_search\"`) — аудиофрагмент едет в `input_files`,\nздесь только один параметр:\n\n| Ключ | Тип | Дефолт | Назначение |\n| --- | --- | --- | --- |\n| `top` | integer `1..50` | `10` | Сколько похожих голосов вернуть |\n\n### Что определяет цену\n\nСчитается **число генераций**, а не их размер:\n\n| Тип | Цена | На цену НЕ влияют |\n| --- | --- | --- |\n| `sfx` | Одна ставка сервиса за заказ | `duration_seconds`, длина `text` |\n| `music` | Ставка × `n_variants` | `music_length_ms`, длина `prompt`, `lyrics_text` |\n| `voice_search` | Одна ставка за заказ | Длительность записи, `top` |\n\nПятиминутный трек стоит столько же, сколько десятисекундный, — платите за\nобращение к модели. Фактически списанное придёт в `price_units` созданного заказа.\n\nСервер домешивает к вашим значениям `default_config` сервиса (ваши приоритетнее) —\nотключается флагом `skip_service_defaults`. Значение вне диапазона или enum → `422`\n**до** биллинга, заказ не создаётся и деньги не резервируются.",
                        "example": {
                            "text": "Тяжёлая деревянная дверь скрипит и захлопывается",
                            "duration_seconds": 4
                        }
                    },
                    "config_override": {
                        "type": "object",
                        "description": "Разовое отклонение от настроек шаблона — **единственный способ поменять параметры\nпод конкретный заказ, не создавая новый шаблон**. Форма — тот же JSON-объект, что\nи `config` шаблона; применяется поверх него только к этому заказу.\n\nНапример, озвучить один заказ другим голосом:\n`{\"tts_settings\": {\"voice_id\": \"<OTHER_VOICE_ID>\"}}` — остальной конфиг шаблона\nсохранится.\n\nПути, которых нет в схеме сервиса, **молча отбрасываются** (опечатка в ключе\nвыглядит как «настройка не применилась»); оставшиеся значения валидируются по\nсхеме — вне диапазона или enum → `422`. Работает только вместе с `template_id`:\nу template-less `sfx`/`music` все параметры и так в `task_data`.\n\n> **Присылайте только те ключи, которые меняете.** Явный `null` — не «оставить как\n> в шаблоне», а «стереть»: он перезапишет значение шаблона на пустое для этого\n> заказа. Клиент, который сериализует всю форму настроек целиком и кладёт `null` в\n> незаполненные поля, таким образом молча сбросит половину шаблона. Отсутствующий\n> ключ — единственный способ сказать «не трогать».\n\n### Абзацный режим озвучки\n\nПо умолчанию текст режется на фрагменты подряд, по техническому размеру, и\nграницы абзацев роли не играют. Абзацный режим меняет порядок: сначала текст\nделится по **пустой строке** на абзацы, и уже каждый абзац режется внутри себя.\n\nКлюч лежит НЕ внутри `tts_settings`, а рядом с ним, в корне `config_override`:\n\n```json\n{\"generation_mode\": \"paragraph\"}\n```\n\n| Значение | Что делает |\n| --- | --- |\n| `paragraph` | Абзац — самостоятельная единица генерации |\n| `default` | Сплошной рез по размеру фрагмента (так же, как без ключа) |\n\nПоддерживают `elevenlabs` и `heygen`; остальные сервисы ключ игнорируют —\nотказа не будет, режим просто не включится.\n\n**Что изменится в ответе:** у заказа появится `paragraphs[]` (по каждому абзацу —\nиндекс, число фрагментов, общий статус и прогресс), у фрагментов —\n`paragraph_index`.\n\n> **Влияет на цену.** Минимальный тарифицируемый объём применяется к КАЖДОМУ\n> абзацу отдельно, а не к заказу целиком: пять коротких абзацев стоят пять\n> минимумов, тогда как тот же текст в обычном режиме обошёлся бы в один.\n\n**Влияет и на скорость.** Абзац — самостоятельная задача, и одновременно\nисполняются не больше 10 однофрагментных абзацев плюс 5 многофрагментных на заказ;\nостальные ждут освобождения места. Текст из сотни коротких абзацев пойдёт\nволнами, зато первые результаты придут раньше, чем при сплошной нарезке.\n\nТот же ключ принимает и `config` шаблона — тогда режим станет постоянным для\nвсех заказов по этому шаблону. В `GET /templates/config-options` он не\nперечислен: справочник описывает настройки сервиса, а режим задаётся на заказ.",
                        "example": {
                            "tts_settings": {
                                "voice_id": "EXAVITQu4vr4xnSDxMaL"
                            }
                        }
                    },
                    "skip_service_defaults": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "Не подмешивать `default_config` сервиса к `task_data`. Пришлите `true`, если у вас\nуже есть полный набор параметров и воркер должен получить ровно его — сервер не\nдобавит ни одного ключа по умолчанию.\n\nФлаг сохраняется на заказе: `POST /orders/{order}/retry` воспроизведёт то же\nповедение. На заказы **по шаблону** не влияет — там конфиг берётся из `template.config`\nи `config_override`, а не из настроек сервиса.",
                        "default": false
                    },
                    "input_text": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Текст задачи. Для `tts` — то, что будет озвучено; сервер сам режет его на чанки.\n\n**Каналов текста три, и они равноправны** — проверки (капы пауз, «текст нечем\nозвучить») применяются к каждому:\n\n| Канал | Когда использовать |\n| --- | --- |\n| `input_text` | Обычный способ: текст прямо в запросе |\n| `.txt` в `input_files[]` | Длинный текст файлом; содержимое читается как текст задачи |\n| `task_data.text` | Только для заказа без шаблона |\n\nЧто происходит с текстом до нарезки и как разметка внутри него влияет на цену —\nв разделах «Разметка внутри текста» и «Как режется текст» описания этого метода.\n\nДля template-less `sfx`/`music` это поле **не используется** — описание звука\nкладите в `task_data.text` (SFX) или `task_data.prompt` (Music).",
                        "example": "Здравствуйте! Рады видеть вас в Lumean."
                    },
                    "confirm_payg_topup": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "Согласие доплатить из LMC-баланса, когда квоты подписки не хватило на весь заказ.\n**В норме не нужно:** добор списывается автоматически, а поле игнорируется. Оно\nдействует только в режиме отката к явному подтверждению, который может включить\nоператор платформы, — правила ниже описывают этот режим.\n\n**Порядок списания всегда один: сначала квота подписки, LMC — только за остаток.**\nЕсли квоты хватает, флаг ничего не меняет и баланс не трогается вообще.\n\n**Один флаг ничего не спишет.** Согласие действительно только вместе со свежим\n`quote_token` из ответа `402`, привязанным к вам, сервису и точному объёму задачи.\nПрисланный без токена (или с истёкшим/чужим) `confirm_payg_topup: true` снова\nвернёт `402` — списания не произойдёт.\n\nРабочий цикл: отправили заказ → `402` с `shortfall_tokens`, `shortfall_lmc`,\n`quote_token`, `expires_at` → показали сумму пользователю → повторили **тот же**\nзапрос с `confirm_payg_topup: true` и этим `quote_token`. Токен живёт ~60 секунд.\n\nСогласие — верхняя граница: если к моменту списания добор подешевел, спишется\nфактическая сумма; если подорожал (параллельный заказ съел квоту) — `409`\n`quote_mismatch`, запросите `402` заново.",
                        "default": false
                    },
                    "quote_token": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Подписанная котировка добора из ответа `402` — передавайте строку «как есть»,\nвместе с `confirm_payg_topup: true`.\n\nТокен привязан к пользователю, сервису и объёму задачи и живёт ~60 секунд.\nПросроченный, изменённый или выданный на другой заказ токен не действует:\nзапрос вернёт `402` с новой котировкой."
                    },
                    "input_files": {
                        "type": [
                            "array",
                            "null"
                        ],
                        "description": "Входные файлы — только `multipart/form-data`, до 20 МБ на файл. Одиночный файл\nможно прислать полем `input_file`.\n\n| Тип задачи | Файл |\n| --- | --- |\n| `tts` (озвучка) | `.txt` — **полноценная замена `input_text`**: содержимое читается как текст озвучки |\n| `voice_search` | Аудиофрагмент, **обязателен**. Тип проверяется по содержимому, не по расширению; чужой формат (картинка, архив) → `422` до списания. Длиннее 60 секунд — обрезается, признак придёт в результате |\n| `sfx`, `music` | Не используются — всё описание в `task_data` |",
                        "items": {
                            "type": "string",
                            "format": "binary",
                            "contentMediaType": "application/octet-stream",
                            "maxLength": 20480
                        }
                    }
                },
                "title": "StoreOrderRequest"
            },
            "StoreTemplateRequest": {
                "type": "object",
                "description": "Новый шаблон генерации. `config` валидируется по схеме сервиса — структуру\nдля нужного `service_key` берите из `GET /templates/config-options`.",
                "properties": {
                    "folder_id": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "UUID вашей папки. Не передан — шаблон создаётся в корне."
                    },
                    "service_key": {
                        "type": "string",
                        "description": "Код сервиса, под который создаётся шаблон: короткая латинская строка — `elevenlabs`, `heygen`, `lumimg`, `sfx`, `music`. Доступные вам коды перечисляет `GET /usage` в поле `service_code`; от этого выбора зависит, что примет `config`.",
                        "example": "elevenlabs"
                    },
                    "name": {
                        "type": "string",
                        "maxLength": 255,
                        "description": "Название шаблона, до 255 символов. Видно в `GET /templates` и в интерфейсе.",
                        "example": "Диктор — женский, спокойный"
                    },
                    "is_public": {
                        "type": "boolean",
                        "description": "Опубликовать шаблон в общем каталоге. По умолчанию `false` — шаблон виден только вам.",
                        "default": false
                    },
                    "config": {
                        "type": "object",
                        "description": "Настройки генерации. Структура зависит от `service_key`; допустимые ключи, диапазоны\nи списки значений отдаёт `GET /templates/config-options?service=<код>` — берите их\nоттуда, а не угадывайте.\n\nМинимальный валидный `config` для `elevenlabs`:\n`{\"tts_settings\": {\"mode\": \"mode_v1\", \"model_id\": \"eleven_multilingual_v2\", \"voice_id\": \"<VOICE_ID>\", \"advanced_voice_settings\": true, \"voice_settings\": {\"stability\": 0.5, \"similarity_boost\": 0.75, \"use_speaker_boost\": true, \"speed\": 1.0}}}`\n\n### Тонкие настройки голоса включаются флагом\n\nЧто из `voice_settings` доезжает до движка озвучки, зависит от модели и флага\n`tts_settings.advanced_voice_settings` (по умолчанию `false`):\n\n| Модель | Без флага | С флагом |\n| --- | --- | --- |\n| `eleven_v3`, `eleven_v3_beta` | `stability` | все пять |\n| `eleven_v4`, `eleven_v4_exp` | `stability`, `similarity_boost` | все пять |\n| остальные | все пять | все пять |\n\nНе доехавшие значения сохранятся в шаблоне и пройдут валидацию, но на звук не\nповлияют. Ошибки не будет: это выглядит как «настройка не работает» — чаще всего\nсо `speed` на v3/v4.\n\nЗависимость той же природы есть и у `voice_settings.stability`: на моделях\nсемейства `eleven_v3` шаг равен 0.5, то есть принимаются только `0`, `0.5` и `1`.\n\n### Сшивка просодии между фрагментами\n\nДлинный текст режется на фрагменты, и каждый озвучивается своим запросом —\nпоэтому на стыках слышен перепад интонации. Два необязательных булевых флага\nпросят движок учитывать соседей: `tts_settings.prev_next_text` передаёт текст\nсоседних фрагментов, `tts_settings.request_ids` — идентификаторы соседних\nгенераций. Оба по умолчанию `false`. Флаги влияют только на звучание стыков,\nстоимость и разбиение на фрагменты не меняют.\n\n**Включить можно только ОДИН из них.** Оба сразу — `422`: получив и текст, и\nидентификаторы, движок использует только идентификаторы и текст игнорирует,\nтак что «включено оба» было бы тихо выключенным `prev_next_text`.\n\n### Нормализация текста\n\n`tts_settings.apply_text_normalization` — три режима: `auto` (движок сам решает,\nнужна ли нормализация, например читать ли числа словами), `on` (применять\nвсегда), `off` (пропускать). По умолчанию `auto`."
                    }
                },
                "required": [
                    "service_key",
                    "name",
                    "config"
                ],
                "title": "StoreTemplateRequest"
            },
            "TemplateList": {
                "title": "TemplateList",
                "type": "object",
                "properties": {
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Template"
                        },
                        "description": "Записи текущей страницы в порядке сортировки списка."
                    },
                    "current_page": {
                        "type": "integer",
                        "description": "Номер текущей страницы, с единицы."
                    },
                    "last_page": {
                        "type": "integer",
                        "description": "Номер последней страницы. Обход списка закончен, когда `current_page` равен `last_page`."
                    },
                    "per_page": {
                        "type": "integer",
                        "description": "Размер страницы, применённый сервером."
                    },
                    "total": {
                        "type": "integer",
                        "description": "Сколько записей всего по фильтру."
                    }
                },
                "required": [
                    "items",
                    "current_page",
                    "last_page",
                    "per_page",
                    "total"
                ]
            },
            "TemplateItem": {
                "anyOf": [
                    {
                        "type": "object",
                        "properties": {
                            "type": {
                                "type": "string",
                                "const": "template"
                            },
                            "id": {
                                "type": "string"
                            },
                            "name": {
                                "type": "string"
                            },
                            "slug": {
                                "type": "string"
                            },
                            "service_key": {
                                "type": "string"
                            },
                            "service": {
                                "anyOf": [
                                    {
                                        "description": "Сервис в сокращённом виде: без схем конфигурации. Полные схемы — `GET /templates/config-options`.",
                                        "$ref": "#/components/schemas/Service"
                                    },
                                    {
                                        "type": "null"
                                    }
                                ]
                            },
                            "config": {
                                "anyOf": [
                                    {
                                        "type": "object"
                                    },
                                    {
                                        "type": "string"
                                    }
                                ]
                            },
                            "is_public": {
                                "type": "string"
                            },
                            "folder_id": {
                                "type": "string"
                            },
                            "created_at": {
                                "type": "string"
                            },
                            "updated_at": {
                                "type": "string"
                            }
                        },
                        "required": [
                            "type",
                            "id",
                            "name",
                            "slug",
                            "service_key",
                            "service",
                            "config",
                            "is_public",
                            "folder_id",
                            "created_at",
                            "updated_at"
                        ]
                    },
                    {
                        "type": "object",
                        "properties": {
                            "type": {
                                "type": "string",
                                "const": "folder"
                            },
                            "id": {
                                "type": "string"
                            },
                            "name": {
                                "type": "string"
                            },
                            "templates_count": {
                                "anyOf": [
                                    {
                                        "type": "string"
                                    },
                                    {
                                        "type": "integer",
                                        "enum": [
                                            0
                                        ]
                                    }
                                ]
                            },
                            "created_at": {
                                "type": "string"
                            },
                            "updated_at": {
                                "type": "string"
                            }
                        },
                        "required": [
                            "type",
                            "id",
                            "name",
                            "templates_count",
                            "created_at",
                            "updated_at"
                        ]
                    }
                ],
                "title": "TemplateItem"
            },
            "Template": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "UUID шаблона. Его кладут в `template_id` при создании заказа."
                    },
                    "user_id": {
                        "type": "integer",
                        "description": "ID владельца шаблона."
                    },
                    "slug": {
                        "type": "string",
                        "description": "Короткий публичный идентификатор для расшаривания. `null` у шаблона, которым не делились."
                    },
                    "name": {
                        "type": "string",
                        "description": "Название шаблона — только для человека, на поведение генерации не влияет."
                    },
                    "service_key": {
                        "type": "string",
                        "description": "Код сервиса, по схеме которого валидируется `config`: `elevenlabs`, `lumimg` и т. д."
                    },
                    "service": {
                        "anyOf": [
                            {
                                "description": "Сервис шаблона справочно: название и тарификация, без схем конфигурации. `null` означает, что сервиса с таким кодом больше нет.",
                                "$ref": "#/components/schemas/Service"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "description": "Сервис шаблона справочно: название и тарификация, без схем конфигурации. `null` означает, что сервиса с таким кодом больше нет."
                    },
                    "config": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "Настройки генерации: голос, модель, скорость и прочее — набор ключей задаёт сервис. Допустимые значения смотрите в `GET /templates/config-options?service=<код>`, значение вне схемы отклоняется с `422`. В заказе (`template` в `GET /orders/{order}`) это настройки самого заказа — снимок на момент его создания с `config_override`, а не текущий конфиг шаблона (у заказов, созданных до конца сентября 2026 года, — текущий конфиг шаблона с `config_override`)."
                    },
                    "is_public": {
                        "type": "boolean",
                        "description": "Опубликован ли шаблон в общем каталоге."
                    },
                    "folder_id": {
                        "type": "string",
                        "description": "UUID папки, в которой лежит шаблон, либо `null` для корня. Структура папок видна в `GET /templates/browse`."
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда шаблон создан, ISO 8601."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда шаблон последний раз менялся, ISO 8601."
                    }
                },
                "required": [
                    "id",
                    "user_id",
                    "slug",
                    "name",
                    "service_key",
                    "service",
                    "config",
                    "is_public",
                    "folder_id",
                    "created_at",
                    "updated_at"
                ],
                "title": "Template"
            },
            "UpdateTemplateRequest": {
                "type": "object",
                "description": "Частичное обновление шаблона: передавайте только изменяемые поля. `service_key`\nи `config` меняются **вместе** — конфиг валидируется по схеме нового сервиса.",
                "properties": {
                    "folder_id": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "UUID папки, в которую переместить шаблон. `null` — перенести в корень."
                    },
                    "service_key": {
                        "type": "string",
                        "description": "Новый код сервиса. Меняется только вместе с `config` — конфиг валидируется по схеме нового сервиса.",
                        "example": "elevenlabs"
                    },
                    "name": {
                        "type": "string",
                        "maxLength": 255,
                        "description": "Новое название шаблона, до 255 символов."
                    },
                    "is_public": {
                        "type": "boolean",
                        "description": "Опубликовать шаблон в общем каталоге или снять с публикации."
                    },
                    "config": {
                        "type": "object",
                        "description": "Новые настройки генерации целиком (не частичный патч). Допустимая структура — `GET /templates/config-options?service=<код>`."
                    }
                },
                "title": "UpdateTemplateRequest"
            },
            "UserLibraryList": {
                "title": "UserLibraryList",
                "type": "object",
                "properties": {
                    "items": {
                        "type": "array",
                        "items": {
                            "type": "object"
                        },
                        "description": "Записи текущей страницы в порядке сортировки списка."
                    },
                    "current_page": {
                        "type": "integer",
                        "description": "Номер текущей страницы, с единицы."
                    },
                    "last_page": {
                        "type": "integer",
                        "description": "Номер последней страницы. Обход списка закончен, когда `current_page` равен `last_page`."
                    },
                    "per_page": {
                        "type": "integer",
                        "description": "Размер страницы, применённый сервером."
                    },
                    "total": {
                        "type": "integer",
                        "description": "Сколько записей всего по фильтру."
                    }
                },
                "required": [
                    "items",
                    "current_page",
                    "last_page",
                    "per_page",
                    "total"
                ]
            },
            "VoiceLanguage": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Внутренний идентификатор языковой версии голоса."
                    },
                    "language_code": {
                        "type": "string",
                        "description": "Код языка, ISO-639-1. Он же — ключ в `preview_urls`."
                    },
                    "status": {
                        "type": "string",
                        "description": "Состояние клонирования для этого языка. Заказ принимается только на языке, где клон готов."
                    },
                    "reference_duration_ms": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Длительность референсной записи в миллисекундах — на ней обучен клон."
                    },
                    "model_version": {
                        "type": "integer",
                        "description": "Версия обученной модели. Растёт при переобучении голоса."
                    },
                    "failure_reason": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Почему клонирование не удалось. `null`, если сбоя не было."
                    },
                    "clone_started_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда клонирование началось, ISO 8601."
                    },
                    "clone_completed_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда клонирование завершилось, ISO 8601. `null` — ещё идёт либо не удалось."
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "Когда языковая версия последний раз менялась, ISO 8601."
                    }
                },
                "required": [
                    "id",
                    "language_code",
                    "status",
                    "reference_duration_ms",
                    "model_version",
                    "failure_reason",
                    "clone_started_at",
                    "clone_completed_at",
                    "updated_at"
                ],
                "title": "VoiceLanguage"
            },
            "VoiceTag": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Внутренний идентификатор метки."
                    },
                    "code": {
                        "type": "string",
                        "description": "Код метки — устойчивое значение для фильтрации, в отличие от переводимого `name`."
                    },
                    "name": {
                        "type": "string",
                        "description": "Название метки на языке аккаунта."
                    },
                    "description": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Пояснение к метке на языке аккаунта, если автор его задал."
                    },
                    "color": {
                        "type": "string",
                        "description": "Цвет метки в HEX — подсказка для оформления."
                    },
                    "sort_order": {
                        "type": "integer",
                        "description": "Вес сортировки в списке меток: меньше — выше."
                    }
                },
                "required": [
                    "id",
                    "code",
                    "name",
                    "description",
                    "color",
                    "sort_order"
                ],
                "title": "VoiceTag"
            },
            "AssetKind": {
                "type": "string",
                "enum": [
                    "money",
                    "unit"
                ],
                "title": "AssetKind"
            },
            "BillingMode": {
                "type": "string",
                "enum": [
                    "subscription",
                    "pay_as_you_go"
                ],
                "title": "BillingMode"
            },
            "TaskType": {
                "type": "string",
                "enum": [
                    "tts",
                    "image",
                    "txt2img",
                    "remix",
                    "image_edit",
                    "voice_clone",
                    "sfx",
                    "music"
                ],
                "title": "TaskType"
            }
        },
        "responses": {
            "ValidationException": {
                "description": "Validation error",
                "content": {
                    "application/json": {
                        "schema": {
                            "type": "object",
                            "properties": {
                                "message": {
                                    "type": "string",
                                    "description": "Errors overview."
                                },
                                "errors": {
                                    "type": "object",
                                    "description": "A detailed description of each field that failed validation.",
                                    "additionalProperties": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        }
                                    }
                                }
                            },
                            "required": [
                                "message",
                                "errors"
                            ]
                        }
                    }
                }
            }
        }
    }
}