{
    "openapi": "3.1.0",
    "info": {
        "title": "Вайб-Маркетолог Agent API",
        "version": "1.0.0",
        "description": "Agent API: генерация видео, изображений, музыки, озвучки и текста нейросетями. Каталог моделей, прайс и оценка стоимости открыты без ключа; платные действия требуют ключа Bearer, который выдаётся в личном кабинете (https://vibemarketolog.ru/#agent). У ключа есть суточный предел траты. Параметры моделей передаются ПЛОСКО в теле запроса — вложенного объекта params нет; состав параметров каждой модели отдаёт GET /capabilities. Программное управление рекламой (кампании Яндекс Директа, автопилот, доступы к рекламным кабинетам, исследование спроса) в этот контракт не входит и предоставляется по партнёрскому соглашению: https://vibemarketolog.ru/api",
        "contact": {
            "name": "Вайб-Маркетолог",
            "url": "https://vibemarketolog.ru"
        }
    },
    "servers": [
        {
            "url": "https://vibemarketolog.ru/api/agent",
            "description": "Production"
        }
    ],
    "security": [
        {
            "AgentBearer": []
        }
    ],
    "tags": [
        {
            "name": "Discovery",
            "description": "Open, no token required"
        },
        {
            "name": "Generate",
            "description": "Spend balance to create content"
        },
        {
            "name": "Voice"
        },
        {
            "name": "Wordstat"
        },
        {
            "name": "Campaigns",
            "description": "Yandex Direct campaigns"
        },
        {
            "name": "Bridge"
        },
        {
            "name": "Webhooks"
        },
        {
            "name": "Landings",
            "description": "«Гипотеза под ключ»: запуск своего лендинга с заявками, A/B-тестом и паспортом качества"
        }
    ],
    "paths": {
        "/health": {
            "get": {
                "tags": [
                    "Discovery"
                ],
                "summary": "Liveness/health",
                "operationId": "health",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Service healthy",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                },
                "x-required-scope": null,
                "x-auth": "без ключа (публичный метод)"
            }
        },
        "/capabilities": {
            "get": {
                "tags": [
                    "Discovery"
                ],
                "summary": "Machine-readable catalog of models, parameters and prices",
                "operationId": "capabilities",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Model catalog",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                },
                "x-required-scope": null,
                "x-auth": "без ключа (публичный метод)"
            }
        },
        "/prices": {
            "get": {
                "tags": [
                    "Discovery"
                ],
                "summary": "Current prices in RUB",
                "operationId": "prices",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Price list",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                },
                "x-required-scope": null,
                "x-auth": "без ключа (публичный метод)"
            }
        },
        "/generate/estimate": {
            "post": {
                "tags": [
                    "Account"
                ],
                "summary": "Dry-run cost estimate incl. affordability (spends 0 RUB). Requires a token because it reflects your balance and daily limit.",
                "operationId": "estimate",
                "x-required-scope": "read",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/GenerateRequest"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Estimate",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/EstimateResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/me": {
            "get": {
                "tags": [
                    "Account"
                ],
                "summary": "Token info",
                "operationId": "me",
                "x-required-scope": "read",
                "responses": {
                    "200": {
                        "description": "Token/user info",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/balance": {
            "get": {
                "tags": [
                    "Account"
                ],
                "summary": "RUB balance and daily spend",
                "operationId": "balance",
                "x-required-scope": "read",
                "responses": {
                    "200": {
                        "description": "Balance",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Balance"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/usage": {
            "get": {
                "tags": [
                    "Account"
                ],
                "summary": "Usage: расход ключа, всех ключей API и аккаунта",
                "operationId": "usage",
                "x-required-scope": "read",
                "responses": {
                    "200": {
                        "description": "Usage",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "period": {
                                            "type": "string"
                                        },
                                        "days": {
                                            "type": "integer"
                                        },
                                        "total_spent": {
                                            "type": "number",
                                            "deprecated": true
                                        },
                                        "total_requests": {
                                            "type": "integer",
                                            "deprecated": true
                                        },
                                        "by_day": {
                                            "type": "array",
                                            "deprecated": true,
                                            "items": {
                                                "type": "object"
                                            }
                                        },
                                        "key": {
                                            "allOf": [
                                                {
                                                    "type": "object",
                                                    "properties": {
                                                        "spent": {
                                                            "type": "number",
                                                            "description": "Итог: списано минус вернулось, ₽"
                                                        },
                                                        "charged": {
                                                            "type": "number",
                                                            "description": "Списано, ₽"
                                                        },
                                                        "refunded": {
                                                            "type": "number",
                                                            "description": "Вернулось, ₽"
                                                        },
                                                        "bonus": {
                                                            "type": "number",
                                                            "description": "Из итога оплачено бонусами, ₽"
                                                        },
                                                        "operations": {
                                                            "type": "integer",
                                                            "description": "Число платных операций"
                                                        }
                                                    }
                                                },
                                                {
                                                    "type": "object",
                                                    "properties": {
                                                        "key_id": {
                                                            "type": "integer"
                                                        },
                                                        "name": {
                                                            "type": "string"
                                                        },
                                                        "by_day": {
                                                            "type": "array",
                                                            "items": {
                                                                "type": "object",
                                                                "properties": {
                                                                    "date": {
                                                                        "type": "string",
                                                                        "format": "date"
                                                                    },
                                                                    "spent": {
                                                                        "type": "number"
                                                                    },
                                                                    "operations": {
                                                                        "type": "integer"
                                                                    }
                                                                }
                                                            }
                                                        },
                                                        "by_category": {
                                                            "type": "array",
                                                            "items": {
                                                                "type": "object",
                                                                "properties": {
                                                                    "category": {
                                                                        "type": "string"
                                                                    },
                                                                    "label": {
                                                                        "type": "string"
                                                                    },
                                                                    "spent": {
                                                                        "type": "number"
                                                                    },
                                                                    "operations": {
                                                                        "type": "integer"
                                                                    },
                                                                    "share": {
                                                                        "type": "number"
                                                                    }
                                                                }
                                                            }
                                                        },
                                                        "by_model": {
                                                            "type": "array",
                                                            "items": {
                                                                "type": "object",
                                                                "properties": {
                                                                    "model": {
                                                                        "type": "string"
                                                                    },
                                                                    "spent": {
                                                                        "type": "number"
                                                                    },
                                                                    "operations": {
                                                                        "type": "integer"
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            ]
                                        },
                                        "api": {
                                            "allOf": [
                                                {
                                                    "type": "object",
                                                    "properties": {
                                                        "spent": {
                                                            "type": "number",
                                                            "description": "Итог: списано минус вернулось, ₽"
                                                        },
                                                        "charged": {
                                                            "type": "number",
                                                            "description": "Списано, ₽"
                                                        },
                                                        "refunded": {
                                                            "type": "number",
                                                            "description": "Вернулось, ₽"
                                                        },
                                                        "bonus": {
                                                            "type": "number",
                                                            "description": "Из итога оплачено бонусами, ₽"
                                                        },
                                                        "operations": {
                                                            "type": "integer",
                                                            "description": "Число платных операций"
                                                        }
                                                    }
                                                },
                                                {
                                                    "type": "object",
                                                    "properties": {
                                                        "by_key": {
                                                            "type": "array",
                                                            "items": {
                                                                "type": "object",
                                                                "properties": {
                                                                    "key_id": {
                                                                        "type": [
                                                                            "integer",
                                                                            "null"
                                                                        ]
                                                                    },
                                                                    "name": {
                                                                        "type": "string"
                                                                    },
                                                                    "prefix": {
                                                                        "type": [
                                                                            "string",
                                                                            "null"
                                                                        ]
                                                                    },
                                                                    "kind": {
                                                                        "type": [
                                                                            "string",
                                                                            "null"
                                                                        ]
                                                                    },
                                                                    "spent": {
                                                                        "type": "number"
                                                                    },
                                                                    "operations": {
                                                                        "type": "integer"
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            ]
                                        },
                                        "account": {
                                            "type": "object",
                                            "properties": {
                                                "spent": {
                                                    "type": "number",
                                                    "description": "₽, после вычета возвратов"
                                                },
                                                "services": {
                                                    "type": "number",
                                                    "description": "₽, после вычета возвратов"
                                                },
                                                "purchases": {
                                                    "type": "number",
                                                    "description": "₽, после вычета возвратов"
                                                },
                                                "bonus_expired": {
                                                    "type": "number",
                                                    "description": "Сгоревшие бонусы — не трата"
                                                },
                                                "manual_debits": {
                                                    "type": "number"
                                                }
                                            }
                                        },
                                        "range": {
                                            "type": "object",
                                            "properties": {
                                                "from": {
                                                    "type": "string",
                                                    "format": "date-time"
                                                },
                                                "to": {
                                                    "type": "string",
                                                    "format": "date-time"
                                                },
                                                "tz": {
                                                    "type": "string"
                                                }
                                            }
                                        },
                                        "history_from": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "format": "date",
                                            "description": "С какой даты есть история операций по ключам"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                },
                "description": "Блоки key (этот ключ), api (все ключи аккаунта) и account (весь аккаунт: services — работа, purchases — покупки тарифов и серверов, bonus_expired — сгоревшие бонусы, manual_debits — ручные списания) считаются по журналу операций ключей: возвраты вычтены, сутки по Москве. Поля total_spent / total_requests / by_day сохранены для совместимости и устарели: они складывают все списания аккаунта, включая сгорание бонусов, без вычета возвратов, в сутках UTC.",
                "parameters": [
                    {
                        "name": "period",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "today",
                                "week",
                                "month"
                            ],
                            "default": "month"
                        }
                    },
                    {
                        "name": "days",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "enum": [
                                0,
                                1,
                                7,
                                30,
                                90
                            ]
                        },
                        "description": "Период: 1 — сегодня, 7, 30, 90, 0 — вся история. Важнее period."
                    }
                ]
            }
        },
        "/usage/operations": {
            "get": {
                "tags": [
                    "Account"
                ],
                "summary": "Операции: куда ушёл каждый рубль",
                "operationId": "usageOperations",
                "x-required-scope": "read",
                "description": "Каждая платная операция со своими возвратами — в том числе то, чего нет в /generations: Вордстат, Директ, лендинги, тексты, живой голос. Курсорная пагинация от новых к старым.",
                "parameters": [
                    {
                        "name": "scope",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "key",
                                "api"
                            ],
                            "default": "key"
                        },
                        "description": "key — этот ключ, api — все ключи аккаунта"
                    },
                    {
                        "name": "days",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "enum": [
                                0,
                                1,
                                7,
                                30,
                                90
                            ]
                        },
                        "description": "Период: 1 — сегодня, 7, 30, 90, 0 — вся история. Важнее period."
                    },
                    {
                        "name": "period",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "today",
                                "week",
                                "month"
                            ],
                            "default": "month"
                        }
                    },
                    {
                        "name": "category",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "text",
                                "image",
                                "video",
                                "audio",
                                "live",
                                "wordstat",
                                "direct",
                                "landing",
                                "chatbot",
                                "presentation",
                                "server",
                                "other"
                            ]
                        }
                    },
                    {
                        "name": "cursor",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "pattern": "^\\d{1,12}_\\d{1,12}$"
                        },
                        "description": "next_cursor прошлого ответа"
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 50
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Operations",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "scope": {
                                            "type": "string"
                                        },
                                        "days": {
                                            "type": "integer"
                                        },
                                        "tz": {
                                            "type": "string"
                                        },
                                        "next_cursor": {
                                            "type": [
                                                "string",
                                                "null"
                                            ]
                                        },
                                        "operations": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "id": {
                                                        "type": "integer"
                                                    },
                                                    "at": {
                                                        "type": "string",
                                                        "format": "date-time"
                                                    },
                                                    "kind": {
                                                        "type": "string",
                                                        "enum": [
                                                            "charge",
                                                            "refund"
                                                        ]
                                                    },
                                                    "key": {
                                                        "type": "object",
                                                        "properties": {
                                                            "id": {
                                                                "type": [
                                                                    "integer",
                                                                    "null"
                                                                ]
                                                            },
                                                            "name": {
                                                                "type": [
                                                                    "string",
                                                                    "null"
                                                                ]
                                                            }
                                                        }
                                                    },
                                                    "category": {
                                                        "type": "string"
                                                    },
                                                    "category_label": {
                                                        "type": "string"
                                                    },
                                                    "title": {
                                                        "type": "string"
                                                    },
                                                    "model": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ]
                                                    },
                                                    "call": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ]
                                                    },
                                                    "charged": {
                                                        "type": "number"
                                                    },
                                                    "refunded": {
                                                        "type": "number"
                                                    },
                                                    "spent": {
                                                        "type": "number"
                                                    },
                                                    "bonus": {
                                                        "type": "number"
                                                    },
                                                    "refunds": {
                                                        "type": "array",
                                                        "items": {
                                                            "type": "object",
                                                            "properties": {
                                                                "amount": {
                                                                    "type": "number"
                                                                },
                                                                "text": {
                                                                    "type": "string"
                                                                },
                                                                "at": {
                                                                    "type": "string"
                                                                }
                                                            }
                                                        }
                                                    },
                                                    "generation_id": {
                                                        "type": [
                                                            "integer",
                                                            "null"
                                                        ]
                                                    },
                                                    "transaction_id": {
                                                        "type": "integer"
                                                    },
                                                    "request_id": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ]
                                                    },
                                                    "restored": {
                                                        "type": "boolean",
                                                        "description": "Операция до 02.10.2026: ключ определён по журналу запросов"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Неверный параметр"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/generations": {
            "get": {
                "tags": [
                    "Account"
                ],
                "summary": "List generations",
                "operationId": "generations",
                "x-required-scope": "read",
                "responses": {
                    "200": {
                        "description": "List; each item includes display_url — a permanent link to the stored file",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string"
                                        },
                                        "total": {
                                            "type": "integer"
                                        },
                                        "generations": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "id": {
                                                        "type": "integer"
                                                    },
                                                    "type": {
                                                        "type": "string"
                                                    },
                                                    "model": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ]
                                                    },
                                                    "prompt": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ]
                                                    },
                                                    "status": {
                                                        "type": "string"
                                                    },
                                                    "cost": {
                                                        "type": "number"
                                                    },
                                                    "display_url": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "description": "Подписанная ссылка на файл: открывается без входа в кабинет, действует около 7 суток (точный срок — в expires_at). Бывает null, пока файл не сохранён. Для длительного хранения скачивайте файл."
                                                    },
                                                    "result_urls": {
                                                        "type": [
                                                            "array",
                                                            "null"
                                                        ],
                                                        "items": {
                                                            "type": "string"
                                                        }
                                                    },
                                                    "created_at": {
                                                        "type": [
                                                            "string",
                                                            "null"
                                                        ],
                                                        "format": "date-time"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/generation/{id}/status": {
            "get": {
                "tags": [
                    "Account"
                ],
                "summary": "Generation status",
                "operationId": "generationStatus",
                "x-required-scope": "read",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Status",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/GenerationResult"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/voices": {
            "get": {
                "tags": [
                    "Account"
                ],
                "summary": "Voice catalog",
                "operationId": "voices",
                "x-required-scope": "read",
                "responses": {
                    "200": {
                        "description": "Voices",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/safety-check": {
            "post": {
                "tags": [
                    "Account"
                ],
                "summary": "Moderate a prompt before generation",
                "operationId": "safetyCheck",
                "x-required-scope": "read",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "prompt": {
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "prompt"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Verdict",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/generate": {
            "post": {
                "tags": [
                    "Generate"
                ],
                "summary": "Create content (video/image/voice/music/text). Charges the RUB balance.",
                "operationId": "generate",
                "x-required-scope": "generate",
                "x-throttle": {
                    "group": "agent_generate",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/GenerateRequest"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Completed synchronously",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/GenerationResult"
                                }
                            }
                        }
                    },
                    "202": {
                        "description": "Accepted (async); poll status_url or await webhook",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/GenerationAccepted"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/InsufficientBalance"
                    },
                    "403": {
                        "description": "Scope not granted or IP not allowed"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                },
                "callbacks": {
                    "generationWebhook": {
                        "{$request.body#/webhook_url}": {
                            "post": {
                                "summary": "Signed delivery of the generation result",
                                "parameters": [
                                    {
                                        "name": "X-Vibe-Signature",
                                        "in": "header",
                                        "schema": {
                                            "type": "string"
                                        },
                                        "description": "hex HMAC-SHA256(rawBody, webhook_secret)"
                                    }
                                ],
                                "requestBody": {
                                    "content": {
                                        "application/json": {
                                            "schema": {
                                                "$ref": "#/components/schemas/GenerationResult"
                                            }
                                        }
                                    }
                                },
                                "responses": {
                                    "200": {
                                        "description": "Acknowledged"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/upload-media": {
            "post": {
                "tags": [
                    "Generate"
                ],
                "summary": "Upload a media file for use as input",
                "operationId": "uploadMedia",
                "x-required-scope": "generate",
                "requestBody": {
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "file": {
                                        "type": "string",
                                        "format": "binary"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Uploaded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/keywords-suggest": {
            "post": {
                "tags": [
                    "Campaigns"
                ],
                "summary": "Suggest keywords (Wordstat) — 49 ₽ per unique phrase",
                "operationId": "keywordsSuggest",
                "x-required-scope": "generate",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Suggestions",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                },
                "description": "Frequency and related Wordstat queries per phrase. Price: 49 ₽ per unique phrase (never below 2× provider cost); phrases without a Wordstat answer are refunded; Wordstat unavailable → 503 wordstat_unavailable with full refund.",
                "x-price-rub": {
                    "per_phrase": 49
                }
            }
        },
        "/semantics": {
            "post": {
                "tags": [
                    "Campaigns"
                ],
                "summary": "Site semantics for a landing page (Wordstat) — 49 ₽ quick / 149 ₽ deep",
                "description": "Keywords from Yandex Wordstat grouped by landing-page blocks (hero, pricing, faq, lead_form…), with frequency, negative words and, for deep, 24-month seasonality and A/B headlines per cluster. dry_run=true returns the price without charging. Same idempotency_key replays the previous answer without a second charge. Wordstat unavailable → 503 wordstat_unavailable (full refund); no phrases → 422 no_phrases (full refund). MCP twin: build_semantics.",
                "operationId": "buildSemantics",
                "x-required-scope": "generate",
                "x-price-rub": {
                    "quick": 49,
                    "deep": 149
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "topic": {
                                        "type": "string",
                                        "maxLength": 300,
                                        "description": "What is sold and where, e.g. \"кухни на заказ в Казани\""
                                    },
                                    "seeds": {
                                        "type": "array",
                                        "maxItems": 3,
                                        "items": {
                                            "type": "string",
                                            "maxLength": 120
                                        }
                                    },
                                    "depth": {
                                        "type": "string",
                                        "enum": [
                                            "quick",
                                            "deep"
                                        ],
                                        "default": "quick"
                                    },
                                    "region_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "integer"
                                        },
                                        "description": "Yandex region ids: 213 Moscow, 2 St Petersburg, 43 Kazan"
                                    },
                                    "negative_words": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        }
                                    },
                                    "ab_headlines": {
                                        "type": "boolean"
                                    },
                                    "dry_run": {
                                        "type": "boolean"
                                    },
                                    "idempotency_key": {
                                        "type": "string",
                                        "maxLength": 120
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "keywords[], clusters[] {name, block, intent, head, freq_sum, phrases[], headline_a?, headline_b?}, negative_suggestions[], seasonality[] (deep), cost, balance_after",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "description": "insufficient_balance / email_confirmation_required"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "503": {
                        "description": "wordstat_unavailable — full refund"
                    },
                    "500": {
                        "$ref": "#/components/responses/InternalError"
                    }
                }
            }
        },
        "/identify": {
            "post": {
                "tags": [
                    "Discovery"
                ],
                "summary": "Агент сообщает о себе: имя, версия, площадка. Появляется в списке агентов владельца в кабинете.",
                "operationId": "post_identify",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Имя, версия и тип агента"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/identity/claim": {
            "post": {
                "tags": [
                    "Discovery"
                ],
                "summary": "Получить ссылку, по которой человек подтвердит ключ агента в кабинете и откроет ему траты.",
                "operationId": "post_identity_claim",
                "x-required-scope": null,
                "x-auth": "без ключа (публичный метод)",
                "x-throttle": {
                    "group": "agent_public",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Идентификатор агента"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/inbox": {
            "get": {
                "tags": [
                    "Bridge"
                ],
                "summary": "То же, что /list, — совместимый псевдоним.",
                "operationId": "get_inbox",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_inbox",
                    "scope": "per-token"
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/inbox/{id}/reply": {
            "post": {
                "tags": [
                    "Bridge"
                ],
                "summary": "Ответить на входящее сообщение от имени агента.",
                "operationId": "post_inbox_id_reply",
                "x-required-scope": "write",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Текст ответа и, необязательно, действия для CRM"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/list": {
            "get": {
                "tags": [
                    "Bridge"
                ],
                "summary": "Сообщения, ожидающие ответа агента (Telegram, Битрикс24, сайт).",
                "operationId": "get_list",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/message": {
            "post": {
                "tags": [
                    "Bridge"
                ],
                "summary": "Отправить сообщение человеку в подключённый канал.",
                "operationId": "post_message",
                "x-required-scope": "write",
                "x-throttle": {
                    "group": "agent_message",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Канал, получатель, текст"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/message/{id}": {
            "get": {
                "tags": [
                    "Bridge"
                ],
                "summary": "Состояние отправленного сообщения: доставлено, прочитано, отвечено.",
                "operationId": "get_message_id",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/openapi.json": {
            "get": {
                "tags": [
                    "Discovery"
                ],
                "summary": "Этот контракт в формате OpenAPI 3.1. Доступен без ключа.",
                "operationId": "get_openapi_json",
                "x-required-scope": null,
                "x-auth": "без ключа (публичный метод)",
                "x-throttle": {
                    "group": "agent_public",
                    "scope": "per-token"
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/register": {
            "post": {
                "tags": [
                    "Discovery"
                ],
                "summary": "Саморегистрация агента: сразу выдаёт рабочий пробный ключ с правами read и generate — 5 ₽ в сутки на 3 дня, не больше 15 ₽ суммарно. Этого хватает, чтобы проверить генерацию от начала до конца самой дешёвой моделью изображений (озвучка, видео и музыка в лимит не укладываются). Чтобы снять ограничения, владелец аккаунта забирает ключ по claim_url и пополняет баланс. Без авторизации, 5 запросов в минуту и 20 в сутки на адрес.",
                "operationId": "post_register",
                "x-required-scope": null,
                "x-auth": "без ключа (публичный метод)",
                "x-throttle": {
                    "group": "agent_public",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Название агента и, необязательно, контакт владельца"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/voice/generate": {
            "post": {
                "tags": [
                    "Voice"
                ],
                "summary": "Озвучить текст своим клонированным голосом.",
                "operationId": "post_voice_generate",
                "x-required-scope": "generate",
                "x-throttle": {
                    "group": "agent_generate",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Текст и идентификатор голоса"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/voice/list": {
            "get": {
                "tags": [
                    "Voice"
                ],
                "summary": "Собственные клонированные голоса владельца ключа.",
                "operationId": "get_voice_list",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/voice/regenerate": {
            "post": {
                "tags": [
                    "Voice"
                ],
                "summary": "Перегенерировать озвучку с новыми настройками.",
                "operationId": "post_voice_regenerate",
                "x-required-scope": "generate",
                "x-throttle": {
                    "group": "agent_generate",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Идентификатор прежней озвучки и новые параметры"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/voice/status": {
            "get": {
                "tags": [
                    "Voice"
                ],
                "summary": "Состояние клонирования голоса по идентификатору задачи.",
                "operationId": "get_voice_status",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/voice/validate": {
            "post": {
                "tags": [
                    "Voice"
                ],
                "summary": "Проверить образец голоса перед клонированием. Платно: списывается с рублёвого баланса.",
                "operationId": "post_voice_validate",
                "x-required-scope": "generate",
                "x-throttle": {
                    "group": "agent_generate",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Ссылка на образец, отрезок с вокалом, язык"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    },
                    "402": {
                        "$ref": "#/components/responses/InsufficientBalance"
                    }
                }
            }
        },
        "/voice/validate-info": {
            "get": {
                "tags": [
                    "Voice"
                ],
                "summary": "Фраза для подтверждения образца голоса. Доступна только владельцу задачи.",
                "operationId": "get_voice_validate_info",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/voiceover/long/{id}": {
            "get": {
                "tags": [
                    "Voice"
                ],
                "summary": "Состояние длинной озвучки (текст больше 5000 знаков обрабатывается частями и склеивается в один файл).",
                "operationId": "get_voiceover_long_id",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/webhook-test": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "Отправить подписанное тестовое событие, чтобы проверить приёмник. Бесплатно.",
                "operationId": "post_webhook_test",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Необязательный адрес для разовой проверки"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/webhook-url": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "Задать адрес, куда платформа сообщит о готовности долгой операции. Избавляет от опроса статуса.",
                "operationId": "post_webhook_url",
                "x-required-scope": "write",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Адрес приёмника"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/wordstat/dynamic": {
            "post": {
                "tags": [
                    "Wordstat"
                ],
                "summary": "Динамика спроса по фразе: по месяцам, неделям или дням.",
                "operationId": "post_wordstat_dynamic",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Фраза, период, интервал дат, регионы"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/wordstat/geo": {
            "post": {
                "tags": [
                    "Wordstat"
                ],
                "summary": "Распределение спроса по регионам и городам.",
                "operationId": "post_wordstat_geo",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Фраза, тип регионов, устройства"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/wordstat/top": {
            "post": {
                "tags": [
                    "Wordstat"
                ],
                "summary": "Частотность фразы и связанные запросы. Если внешний сервис недоступен, отвечает 503 с полем retry_after — повторять только после указанной паузы.",
                "operationId": "post_wordstat_top",
                "x-required-scope": "read",
                "x-throttle": {
                    "group": "agent_read",
                    "scope": "per-token"
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Фраза, регионы, устройства, число фраз"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Успех"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Право не выдано ключу либо адрес не в списке разрешённых"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationError"
                    },
                    "429": {
                        "description": "Превышена частота, исчерпан суточный лимит трат либо ключ на паузе после серии ошибок. В retry_after — через сколько секунд повторять"
                    }
                }
            }
        },
        "/landings/check": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "Landing passport — free",
                "description": "Real-browser check at 1280/390/360 px before launch: mobile horizontal scroll, CTA on the first screen, lead form with 152-FZ consent, privacy link, no external CDN, first-screen media, readability, H1. vm-gen:ID references to your own generations are resolved. 30 checks per hour.",
                "operationId": "checkLanding",
                "x-required-scope": "generate",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "html": {
                                        "type": "string",
                                        "maxLength": 1500000
                                    },
                                    "html_b": {
                                        "type": "string",
                                        "maxLength": 1500000
                                    },
                                    "assets": {
                                        "type": "array",
                                        "maxItems": 40,
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "path": {
                                                    "type": "string"
                                                },
                                                "content_base64": {
                                                    "type": "string"
                                                }
                                            },
                                            "required": [
                                                "path",
                                                "content_base64"
                                            ]
                                        }
                                    }
                                },
                                "required": [
                                    "html"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "402": {
                        "description": "insufficient_balance / email_confirmation_required"
                    },
                    "403": {
                        "description": "sandbox_not_allowed"
                    },
                    "422": {
                        "description": "passport_failed / bad_input / variant_b_identical"
                    },
                    "429": {
                        "description": "rate_limited / daily_spend_limit_exceeded / already_running"
                    }
                }
            }
        },
        "/landings": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "Launch a landing hypothesis — 990 ₽",
                "description": "Hosts your HTML at /w/{slug}: forms deliver leads to the owner (cabinet, Telegram, email), html_b turns on a 50/50 A/B test on one URL with a significance verdict, a test lead is sent through the real receiver. Passport must pass or nothing is charged. dry_run=true returns passport and price. Same idempotency_key (or same HTML) replays without a second charge. Failure after charge → refund to the original transaction. MCP twin: landing_launch.",
                "operationId": "launchLanding",
                "x-required-scope": "generate",
                "x-price-rub": {
                    "launch": 990
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "title": {
                                        "type": "string",
                                        "maxLength": 200
                                    },
                                    "html": {
                                        "type": "string",
                                        "maxLength": 1500000
                                    },
                                    "html_b": {
                                        "type": "string",
                                        "maxLength": 1500000
                                    },
                                    "hypothesis": {
                                        "type": "string",
                                        "maxLength": 1000
                                    },
                                    "slug": {
                                        "type": "string",
                                        "maxLength": 80
                                    },
                                    "assets": {
                                        "type": "array",
                                        "maxItems": 40,
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "path": {
                                                    "type": "string"
                                                },
                                                "content_base64": {
                                                    "type": "string"
                                                }
                                            },
                                            "required": [
                                                "path",
                                                "content_base64"
                                            ]
                                        }
                                    },
                                    "metrika_counter_id": {
                                        "type": "string"
                                    },
                                    "idempotency_key": {
                                        "type": "string",
                                        "maxLength": 120
                                    },
                                    "dry_run": {
                                        "type": "boolean"
                                    }
                                },
                                "required": [
                                    "title",
                                    "html"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "402": {
                        "description": "insufficient_balance / email_confirmation_required"
                    },
                    "403": {
                        "description": "sandbox_not_allowed"
                    },
                    "422": {
                        "description": "passport_failed / bad_input / variant_b_identical"
                    },
                    "429": {
                        "description": "rate_limited / daily_spend_limit_exceeded / already_running"
                    }
                }
            },
            "get": {
                "tags": [
                    "Landings"
                ],
                "summary": "My launched landings",
                "description": "List of launched landing hypotheses.",
                "operationId": "listLandings",
                "x-required-scope": "read",
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "402": {
                        "description": "insufficient_balance / email_confirmation_required"
                    },
                    "403": {
                        "description": "sandbox_not_allowed"
                    },
                    "422": {
                        "description": "passport_failed / bad_input / variant_b_identical"
                    },
                    "429": {
                        "description": "rate_limited / daily_spend_limit_exceeded / already_running"
                    }
                }
            }
        },
        "/landings/{id}": {
            "get": {
                "tags": [
                    "Landings"
                ],
                "summary": "Landing A/B results",
                "description": "Views, leads and conversion per variant, z-test p-value and verdict (early / leading / winner), visits still needed, lead count and next step. MCP twin: landing_status.",
                "operationId": "getLanding",
                "x-required-scope": "read",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "402": {
                        "description": "insufficient_balance / email_confirmation_required"
                    },
                    "403": {
                        "description": "sandbox_not_allowed"
                    },
                    "422": {
                        "description": "passport_failed / bad_input / variant_b_identical"
                    },
                    "429": {
                        "description": "rate_limited / daily_spend_limit_exceeded / already_running"
                    }
                }
            }
        },
        "/landings/{id}/update": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "Update variant A or B — free",
                "description": "Replaces the HTML of a variant (passport required; a broken update never replaces the live page). A running A/B test restarts from zero. Up to 50 updates per landing.",
                "operationId": "updateLanding",
                "x-required-scope": "generate",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "variant": {
                                        "type": "string",
                                        "enum": [
                                            "a",
                                            "b"
                                        ]
                                    },
                                    "html": {
                                        "type": "string",
                                        "maxLength": 1500000
                                    },
                                    "assets": {
                                        "type": "array",
                                        "maxItems": 40,
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "path": {
                                                    "type": "string"
                                                },
                                                "content_base64": {
                                                    "type": "string"
                                                }
                                            },
                                            "required": [
                                                "path",
                                                "content_base64"
                                            ]
                                        }
                                    },
                                    "title": {
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "html"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "402": {
                        "description": "insufficient_balance / email_confirmation_required"
                    },
                    "403": {
                        "description": "sandbox_not_allowed"
                    },
                    "422": {
                        "description": "passport_failed / bad_input / variant_b_identical"
                    },
                    "429": {
                        "description": "rate_limited / daily_spend_limit_exceeded / already_running"
                    }
                }
            }
        },
        "/landings/{id}/pro": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "Pro version of the winner — up to 4 500 ₽ (exact price in dry_run)",
                "description": "Full top-tier site by the website builder based on the winning variant: market research, layout from scratch, images and hero video, quality review; the winner's offer and headline are kept. During a builder promo the price never exceeds the promo price. dry_run=true returns price and the A/B verdict.",
                "operationId": "landingPro",
                "x-required-scope": "generate",
                "x-price-rub": {
                    "pro": 4500
                },
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "variant": {
                                        "type": "string",
                                        "enum": [
                                            "winner",
                                            "a",
                                            "b"
                                        ]
                                    },
                                    "brief": {
                                        "type": "string",
                                        "maxLength": 2000
                                    },
                                    "idempotency_key": {
                                        "type": "string"
                                    },
                                    "dry_run": {
                                        "type": "boolean"
                                    }
                                },
                                "required": []
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "402": {
                        "description": "insufficient_balance / email_confirmation_required"
                    },
                    "403": {
                        "description": "sandbox_not_allowed"
                    },
                    "422": {
                        "description": "passport_failed / bad_input / variant_b_identical"
                    },
                    "429": {
                        "description": "rate_limited / daily_spend_limit_exceeded / already_running"
                    }
                }
            }
        },
        "/uploads/image": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "Put a user image (logo, photo) on our address — free",
                "description": "Downloads a public image by url (SSRF-guarded on the original address and on every redirect, up to 3 redirects, 20 s) or decodes content_base64 (a data: prefix is accepted) and stores it under lk.vibemarketolog.ru/uploads/agent/…. The returned url is permanent: use it in image_input of *-edit models (the logo is kept as is) and directly in <img src> of a landing page — the landing passport accepts it. Only raster PNG, JPG, WEBP, GIF up to 10 MB; the type is detected from the bytes, not from filename. SVG is refused (convert it to PNG). 60 files per day per key, shared with the MCP twin. Files attached by a person in a chat — use POST /uploads/links instead. MCP twin: upload_file.",
                "operationId": "uploadImage",
                "x-required-scope": "generate",
                "x-price-rub": 0,
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "description": "Exactly one source: url or content_base64.",
                                "properties": {
                                    "url": {
                                        "type": "string",
                                        "format": "uri",
                                        "maxLength": 4096,
                                        "description": "Public https link to the image, e.g. https://client-site.ru/logo.png"
                                    },
                                    "content_base64": {
                                        "type": "string",
                                        "description": "Image bytes in base64, with or without a data:image/…;base64, prefix"
                                    },
                                    "filename": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "description": "logo.png — informational for content_base64; the type is detected from the content"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Stored",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UploadedImage"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "sandbox_not_allowed (shared trial key from self-registration: its files would be visible to everyone) / insufficient_scope / ip_not_allowed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "415": {
                        "description": "unsupported_type — not PNG/JPG/WEBP/GIF (SVG included); message names the detected type",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "no_source (neither url nor content_base64) / bad_url (not a public address) / fetch_failed (did not download, redirect to a private address) / bad_file (empty, not downloaded or larger than 10 MB) / validation_failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "daily_limit (60 files per day per key) / fetch_limit (180 download attempts by url per day per key, failed ones included) / rate_limit_exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "store_failed — our server could not save the file (logged on our side); retry in a minute",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/uploads/links": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "One-time upload page for a person — free",
                "description": "Creates a one-time page lk.vibemarketolog.ru/u/{code} for a person whose files are in a chat (the agent sees them but cannot pass them on). Valid 24 hours, up to 10 files and 150 MB: PNG, JPG, WEBP, GIF up to 10 MB; MP4, MOV, WEBM up to 50 MB; SVG is refused; geotags and camera data are removed. kinds lists what is needed — the page builds the phrase for the person itself (\"Нужно для сайта: логотип и фото ваших работ\"). Give url to the person (works on a phone, no login), then read the files with GET /uploads/links/{code}. Files never collected are deleted 7 days after the link expires. Documents, card data and passwords are never requested through the link — such a purpose is refused. 30 links per day per account. MCP twin: upload_link (without code).",
                "operationId": "createUploadLink",
                "x-required-scope": "generate",
                "x-price-rub": 0,
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "kinds": {
                                        "description": "What is needed; an array or a comma-separated string",
                                        "oneOf": [
                                            {
                                                "type": "array",
                                                "items": {
                                                    "type": "string",
                                                    "enum": [
                                                        "logo",
                                                        "work_photos",
                                                        "product_photos",
                                                        "portrait",
                                                        "team_photos",
                                                        "interior",
                                                        "screenshots",
                                                        "video"
                                                    ]
                                                }
                                            },
                                            {
                                                "type": "string"
                                            }
                                        ]
                                    },
                                    "purpose": {
                                        "type": "string",
                                        "maxLength": 120,
                                        "description": "Short clarification for the person, e.g. \"логотип на прозрачном фоне\". No links, phones or e-mails"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Link created: code, url for the person, expires_at, limits",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UploadLink"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "sandbox_not_allowed (shared trial key from self-registration: its files would be visible to everyone) / insufficient_scope / ip_not_allowed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "bad_input — unknown kind, sensitive request (documents, cards, passwords) or a link/phone/e-mail in purpose / validation_failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "daily_limit (30 links per day) / busy — a parallel call of the same account is creating a link: wait Retry-After (2 s) and retry / rate_limit_exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/uploads/links/{code}": {
            "get": {
                "tags": [
                    "Landings"
                ],
                "summary": "Files uploaded through a one-time link — free",
                "description": "Returns permanent addresses of what the person uploaded (url, mime, bytes, width, height, name, kind) for <img src>/<video src> and image_input of *-edit models; the addresses do not expire with the link. Only links created by the same account. Returned files are marked as collected and are no longer removed by the cleanup. Videos from iPhone are transcoded for browsers for 1–2 minutes — while they are in pending, ask again. hint says what to do next. MCP twin: upload_link (with code).",
                "operationId": "getUploadLink",
                "x-required-scope": "read",
                "x-price-rub": 0,
                "parameters": [
                    {
                        "name": "code",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^[A-Za-z0-9]{32}$"
                        },
                        "description": "code from POST /uploads/links"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Link state with files, pending and failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UploadLink"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "sandbox_not_allowed (shared trial key from self-registration: its files would be visible to everyone) / insufficient_scope / ip_not_allowed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "not_found — no such link or it belongs to another account",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/media/check": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "Check speech in your own video or audio — free",
                "description": "Recognizes speech in your own finished generation (video or audio up to 120 s) on our server and compares it with the intended line: word similarity (%), what was not said, what was said instead (with the second; tail=true means the end of the line), consecutive repeats of a word or phrase, where speech ends and where to cut (suggested_trim_at). Verdict ok/warn with advice: a repeat or extra words AFTER the line — POST /media/loop mode=trim with trim_at=suggested_trim_at; a problem in the middle of speech or a distorted end — regenerate (suggested_trim_at is null then). If words_timed=false and the message asks to repeat — call again in a minute. Without expected_text only consecutive repeats are searched; for a background video \"no speech\" is ok. Up to 100 per day, one at a time per account. Service texts name the MCP tools: media_check = POST /media/check, video_loop = POST /media/loop. MCP twin: media_check.",
                "operationId": "checkMediaSpeech",
                "x-required-scope": "read",
                "x-price-rub": 0,
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "generation_id"
                                ],
                                "properties": {
                                    "generation_id": {
                                        "type": "integer",
                                        "description": "Your finished generation (status=complete) — video or audio"
                                    },
                                    "expected_text": {
                                        "type": "string",
                                        "maxLength": 5000,
                                        "description": "The line the character had to say, verbatim (as in the generation prompt)"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Recognized text, similarity, problems and verdict",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/MediaCheckResult"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "sandbox_not_allowed (shared trial key from self-registration: its files would be visible to everyone) / insufficient_scope / ip_not_allowed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "bad_params — no generation_id / media_rejected — not your generation, not finished, too long, recognition busy, daily limit or another call in progress (message says what to do) / validation_failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/media/loop": {
            "post": {
                "tags": [
                    "Landings"
                ],
                "summary": "Seamless loop or trim of your own video — free",
                "description": "Rebuilds your own finished video up to 20 s on our server. mode=boomerang — seamless forward-backward loop for a hero background, without sound (with trim_at — trims first). mode=trim — cut at trim_at with sound (take the point from POST /media/check suggested_trim_at; if it is null — regenerate instead of cutting). Result: H.264, at most 720p, fast start; url is a permanent address on lk.vibemarketolog.ru — put it straight into <video src> (the landing passport accepts it). The file is tied to the source generation and is deleted with it. Do not loop people in boomerang — backwards they look silly. Up to 30 per day, one at a time per account. MCP twin: video_loop.",
                "operationId": "loopMediaVideo",
                "x-required-scope": "generate",
                "x-price-rub": 0,
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "generation_id",
                                    "mode"
                                ],
                                "properties": {
                                    "generation_id": {
                                        "type": "integer",
                                        "description": "Your finished video (status=complete), up to 20 s"
                                    },
                                    "mode": {
                                        "type": "string",
                                        "enum": [
                                            "boomerang",
                                            "trim"
                                        ],
                                        "description": "boomerang — background loop without sound; trim — cut with sound"
                                    },
                                    "trim_at": {
                                        "type": "number",
                                        "description": "Second to cut at (required for trim; optional pre-trim for boomerang)"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Rebuilt video",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/MediaLoopResult"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "sandbox_not_allowed (shared trial key from self-registration: its files would be visible to everyone) / insufficient_scope / ip_not_allowed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "bad_params — no generation_id / media_rejected — wrong mode, no trim_at for trim, video longer than 20 s, not your generation, daily limit or another call in progress / validation_failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "const": "error"
                                        },
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "AgentBearer": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "oc_<user_id>_<random>",
                "description": "Ключ Bearer, который выдаётся в личном кабинете (https://vibemarketolog.ru/#agent). Передаётся только в заголовке Authorization. Проверяется по хешу, с учётом признака активности, срока действия, набора прав и, если он задан, списка разрешённых адресов. Саморегистрация через POST /register выдаёт ключ с правом только на чтение."
            }
        },
        "responses": {
            "Unauthorized": {
                "description": "Ключ не передан, недействителен, отозван или истёк (missing_token / invalid_token)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "InsufficientBalance": {
                "description": "Не хватает рублей на балансе (insufficient_balance)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "ValidationError": {
                "description": "Поля запроса не прошли проверку; разбор — в details (validation_failed)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "Превышена частота запросов или суточный лимит траты ключа (rate_limit_exceeded / daily_spend_limit_exceeded)",
                "headers": {
                    "Retry-After": {
                        "schema": {
                            "type": "integer"
                        },
                        "description": "Через сколько секунд повторить"
                    }
                },
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "Forbidden": {
                "description": "У ключа нет нужного права либо адрес запроса не в списке разрешённых (insufficient_scope / ip_not_allowed)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "NotFound": {
                "description": "Метода нет либо объект не принадлежит владельцу ключа (not_found)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "UpstreamError": {
                "description": "Внешняя модель или инструмент не отработали; списание, если было, возвращено (generation_failed / tool_failed / ai_unavailable)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "InternalError": {
                "description": "Внутренняя ошибка сервиса; повторите позже и сообщите request_id (internal_error)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "description": "Единый ответ об ошибке публичного API. Схема одинакова для всех методов и всех кодов.",
                "required": [
                    "status",
                    "error",
                    "message",
                    "request_id"
                ],
                "additionalProperties": true,
                "properties": {
                    "status": {
                        "type": "string",
                        "const": "error",
                        "description": "Всегда \"error\"."
                    },
                    "error": {
                        "type": "string",
                        "description": "Стабильный машинный код ошибки.",
                        "examples": [
                            "missing_token",
                            "invalid_token",
                            "insufficient_scope",
                            "ip_not_allowed",
                            "not_found",
                            "method_not_allowed",
                            "validation_failed",
                            "invalid_url",
                            "insufficient_balance",
                            "daily_spend_limit_exceeded",
                            "rate_limit_exceeded",
                            "already_running",
                            "generation_failed",
                            "tool_failed",
                            "ai_unavailable",
                            "internal_error"
                        ]
                    },
                    "message": {
                        "type": "string",
                        "description": "Человекочитаемое объяснение на русском: что не так и что сделать."
                    },
                    "details": {
                        "type": "object",
                        "description": "Разбор по полям для ошибок валидации: имя поля → список сообщений.",
                        "additionalProperties": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            }
                        }
                    },
                    "request_id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Идентификатор вызова. Назовите его в обращении в поддержку — по нему находится запись в журнале."
                    }
                }
            },
            "Balance": {
                "type": "object",
                "properties": {
                    "balance": {
                        "type": "number"
                    },
                    "balance_real": {
                        "type": "number"
                    },
                    "balance_bonus": {
                        "type": "number"
                    },
                    "daily_spend_limit": {
                        "type": "number"
                    },
                    "daily_spend_today": {
                        "type": "number"
                    },
                    "daily_spend_remaining": {
                        "type": "number"
                    }
                }
            },
            "EstimateResponse": {
                "type": "object",
                "description": "Бесплатная оценка запроса: ничего не списывается. ВАЖНО: существование модели оценка не проверяет — на незнакомое имя она отвечает valid=true, ценой 0 и предупреждением в warnings. Проверяйте имя модели по GET /capabilities.",
                "properties": {
                    "valid": {
                        "type": "boolean"
                    },
                    "dry_run": {
                        "type": "boolean",
                        "const": true
                    },
                    "model": {
                        "type": "string"
                    },
                    "type": {
                        "type": "string"
                    },
                    "estimated_cost_rub": {
                        "type": "number"
                    },
                    "balance": {
                        "type": "object",
                        "properties": {
                            "current": {
                                "type": "number"
                            },
                            "after": {
                                "type": "number"
                            }
                        }
                    },
                    "daily_spend": {
                        "type": "object",
                        "properties": {
                            "limit": {
                                "type": [
                                    "number",
                                    "null"
                                ]
                            },
                            "today": {
                                "type": "number"
                            },
                            "within_limit": {
                                "type": "boolean"
                            }
                        }
                    },
                    "validation": {
                        "type": "object",
                        "properties": {
                            "media": {
                                "type": "array",
                                "items": {
                                    "type": "object"
                                },
                                "description": "Результат проверки переданных ссылок на медиа."
                            },
                            "required_missing": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                },
                                "description": "Незаполненные обязательные параметры модели."
                            }
                        }
                    },
                    "rejected": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "Параметры, которые модель не принимает."
                    },
                    "valid_params": {
                        "type": [
                            "array",
                            "null"
                        ],
                        "items": {
                            "type": "string"
                        },
                        "description": "Полный список принимаемых параметров; null для модели вне каталога."
                    },
                    "warnings": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    }
                }
            },
            "GenerationAccepted": {
                "type": "object",
                "description": "Ответ на запуск генерации. У длинной озвучки (текст свыше 5000 знаков) форма другая: вместо generation_id приходят long_voiceover=true, voiceover_id и status_url, а состояние опрашивается через GET /voiceover/long/{id}.",
                "properties": {
                    "status": {
                        "type": "string",
                        "examples": [
                            "processing"
                        ]
                    },
                    "generation_id": {
                        "type": "integer",
                        "description": "Номер генерации для опроса состояния."
                    },
                    "task_id": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "cost": {
                        "type": "number",
                        "description": "Сколько списано, в рублях."
                    },
                    "balance_after": {
                        "type": "number",
                        "description": "Баланс после списания."
                    },
                    "replayed": {
                        "type": "boolean",
                        "description": "Есть и равно true, если запрос распознан как повтор по idempotency_key: результат прежний, повторного списания нет."
                    },
                    "ignored_params": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "Параметры, которые модель не приняла (когда strict не задан)."
                    },
                    "long_voiceover": {
                        "type": "boolean",
                        "description": "Признак длинной озвучки."
                    },
                    "voiceover_id": {
                        "type": "integer",
                        "description": "Номер длинной озвучки (вместо generation_id)."
                    },
                    "chars": {
                        "type": "integer"
                    },
                    "chunks": {
                        "type": "integer",
                        "description": "На сколько частей разбит текст."
                    },
                    "status_url": {
                        "type": "string",
                        "format": "uri"
                    },
                    "hint": {
                        "type": "string"
                    }
                }
            },
            "GenerationResult": {
                "type": "object",
                "description": "Состояние генерации. Обратите внимание на ссылки: ни одна из них не вечная. Пользователю показывайте display_url; для длительного хранения скачивайте файл.",
                "properties": {
                    "status": {
                        "type": "string",
                        "enum": [
                            "pending",
                            "processing",
                            "complete",
                            "error"
                        ]
                    },
                    "generation_id": {
                        "type": "integer"
                    },
                    "task_id": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "model": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "type": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "cost": {
                        "type": [
                            "number",
                            "null"
                        ],
                        "description": "Списанная сумма в рублях."
                    },
                    "display_url": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Подписанная ссылка на результат: открывается без входа в кабинет, действует около 7 суток (точный срок — в expires_at), и при каждом запросе состояния выдаётся свежая. Её и показывайте пользователю. В базу сохранять нельзя. Бывает null, пока файл не сохранён."
                    },
                    "file_url": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Ссылка внутри личного кабинета: без срока действия, но требует входа."
                    },
                    "result_url": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Ссылка в том виде, в каком её отдал сервис. У части моделей это адрес поставщика, который перестаёт работать быстро. Не сохранять."
                    },
                    "result_urls": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "То же для моделей, возвращающих несколько файлов."
                    },
                    "expires_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "До какого момента действует display_url."
                    },
                    "thumbnail": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "title": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "duration": {
                        "type": [
                            "number",
                            "null"
                        ],
                        "description": "Длительность в секундах, если применимо."
                    },
                    "error_message": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Причина, если status = error."
                    },
                    "refunded": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "true, если списание возвращено автоматически после сбоя."
                    },
                    "result_object": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "description": "Дополнительные данные отдельных моделей."
                    },
                    "created_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time"
                    },
                    "updated_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time"
                    }
                }
            },
            "GenerateRequest": {
                "type": "object",
                "required": [
                    "type",
                    "model"
                ],
                "description": "Параметры модели передаются ПЛОСКО, на верхнем уровне тела — вложенного объекта params нет. Набор параметров у каждой модели свой: GET /capabilities отдаёт для неё required, optional, limits и enums. Неизвестные параметры при strict=true отклоняются до списания.",
                "additionalProperties": true,
                "properties": {
                    "type": {
                        "type": "string",
                        "enum": [
                            "video",
                            "image",
                            "voice",
                            "music",
                            "text"
                        ],
                        "description": "Тип содержимого."
                    },
                    "model": {
                        "type": "string",
                        "description": "Идентификатор модели. Допустимые значения — из GET /capabilities."
                    },
                    "prompt": {
                        "type": "string",
                        "description": "Описание желаемого результата."
                    },
                    "callback_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Куда сообщить о готовности. Тело подписывается заголовком X-Vibe-Signature. Для длинной озвучки не поддерживается."
                    },
                    "idempotency_key": {
                        "type": "string",
                        "description": "Ключ повторной отправки. Повтор с тем же ключом и тем же телом возвращает прежний результат без нового списания; тот же ключ с другим телом — ошибка 409. Запись живёт 24 часа."
                    },
                    "strict": {
                        "type": "boolean",
                        "description": "Отклонять неизвестные и несовместимые параметры ДО списания. Без него они возвращаются в ignored_params, а деньги списываются."
                    }
                }
            },
            "UploadedImage": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string",
                        "const": "ok"
                    },
                    "url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Permanent address on lk.vibemarketolog.ru"
                    },
                    "mime": {
                        "type": "string",
                        "enum": [
                            "image/png",
                            "image/jpeg",
                            "image/webp",
                            "image/gif"
                        ]
                    },
                    "bytes": {
                        "type": "integer"
                    },
                    "width": {
                        "type": "integer"
                    },
                    "height": {
                        "type": "integer"
                    },
                    "hint": {
                        "type": "string"
                    }
                }
            },
            "UploadLinkFile": {
                "type": "object",
                "properties": {
                    "url": {
                        "type": "string",
                        "format": "uri"
                    },
                    "mime": {
                        "type": "string"
                    },
                    "bytes": {
                        "type": "integer"
                    },
                    "width": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "height": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "name": {
                        "type": "string",
                        "description": "Original name, for showing to the person only"
                    },
                    "kind": {
                        "type": "string",
                        "enum": [
                            "image",
                            "video"
                        ]
                    },
                    "uploaded_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time"
                    }
                }
            },
            "UploadLink": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string",
                        "const": "ok"
                    },
                    "code": {
                        "type": "string"
                    },
                    "url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Page for the person: lk.vibemarketolog.ru/u/{code}"
                    },
                    "kinds": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "asks": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Phrase the page shows to the person"
                    },
                    "purpose": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "expires_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time"
                    },
                    "expires_at_msk": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "expired": {
                        "type": "boolean"
                    },
                    "max_files": {
                        "type": "integer"
                    },
                    "count": {
                        "type": "integer"
                    },
                    "left": {
                        "type": "integer"
                    },
                    "bytes": {
                        "type": "integer"
                    },
                    "max_bytes": {
                        "type": "integer"
                    },
                    "files": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/UploadLinkFile"
                        },
                        "description": "Ready files"
                    },
                    "pending": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {
                                    "type": "string"
                                },
                                "kind": {
                                    "type": "string"
                                },
                                "uploaded_at": {
                                    "type": [
                                        "string",
                                        "null"
                                    ]
                                }
                            }
                        },
                        "description": "Videos still being transcoded"
                    },
                    "failed": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {
                                    "type": "string"
                                },
                                "message": {
                                    "type": "string"
                                }
                            }
                        }
                    },
                    "purged_files": {
                        "type": "integer"
                    },
                    "accepts": {
                        "type": "string",
                        "description": "Only in the create response"
                    },
                    "hint": {
                        "type": "string",
                        "description": "What to do next"
                    }
                }
            },
            "MediaCheckResult": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string",
                        "const": "ok"
                    },
                    "generation_id": {
                        "type": "integer"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "video",
                            "audio"
                        ]
                    },
                    "duration": {
                        "type": "number"
                    },
                    "expected": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "text": {
                        "type": "string",
                        "description": "Recognized speech"
                    },
                    "channel": {
                        "type": "string",
                        "description": "Recognition channel"
                    },
                    "words_timed": {
                        "type": "boolean",
                        "description": "Per-word timings available; without them the cut point cannot be found"
                    },
                    "similarity_pct": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "missing": {
                        "type": "array",
                        "items": [],
                        "description": "Words of expected_text that were not said"
                    },
                    "extra": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "description": "Said but not intended: {text, at}"
                    },
                    "replaced": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "expected": {
                                    "type": "string"
                                },
                                "heard": {
                                    "type": "string"
                                },
                                "at": {
                                    "type": "number"
                                },
                                "tail": {
                                    "type": "boolean"
                                }
                            }
                        }
                    },
                    "repeats": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "phrase": {
                                    "type": "string"
                                },
                                "times": {
                                    "type": "integer"
                                },
                                "at": {
                                    "type": [
                                        "number",
                                        "null"
                                    ]
                                }
                            }
                        }
                    },
                    "speech_end_at": {
                        "type": [
                            "number",
                            "null"
                        ]
                    },
                    "suggested_trim_at": {
                        "type": [
                            "number",
                            "null"
                        ],
                        "description": "Where to cut with POST /media/loop mode=trim; null — regenerate instead"
                    },
                    "verdict": {
                        "type": "string",
                        "enum": [
                            "ok",
                            "warn"
                        ]
                    },
                    "message": {
                        "type": "string",
                        "description": "Verdict in words and what to do"
                    }
                }
            },
            "MediaLoopResult": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string",
                        "const": "ok"
                    },
                    "generation_id": {
                        "type": "integer"
                    },
                    "mode": {
                        "type": "string",
                        "enum": [
                            "boomerang",
                            "trim"
                        ]
                    },
                    "url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Permanent mp4 address for <video src>"
                    },
                    "duration": {
                        "type": "number"
                    },
                    "source_duration": {
                        "type": "number"
                    },
                    "trim_at": {
                        "type": [
                            "number",
                            "null"
                        ]
                    },
                    "bytes": {
                        "type": "integer"
                    },
                    "width": {
                        "type": "integer"
                    },
                    "height": {
                        "type": "integer"
                    },
                    "has_audio": {
                        "type": "boolean"
                    },
                    "loops_left_today": {
                        "type": "integer"
                    },
                    "hint": {
                        "type": "string"
                    }
                }
            }
        }
    }
}