{
  "openapi": "3.0.3",
  "info": {
    "title": "xWires: API к моделям",
    "version": "1.0.0",
    "description": "OpenAI-совместимый API xWires к языковым моделям для команд разработки компании: Chat Completions, Responses с поиском в интернете и список моделей ключа. Подходят официальные SDK OpenAI и любой инструмент, где можно указать свой адрес API и ключ. Руководство с примерами: https://xwires.ai/docs/models.\n\n## Ключ\n\nКлюч команды создаёт админ компании: Кабинет компании → API к моделям. Передавайте его только с сервера, в заголовке `Authorization: Bearer sk-xw-…`. Это не ключ API управления компанией (`xwc_…`): у того свой адрес и своё описание.\n\n## Модели\n\nID модели ставится в поле `model` каждого запроса. Модели, открытые ключу, с контекстом и пределом ответа отдаёт `GET /models`, одну из них по ID — `GET /models/{model}`. Цены компании — только в кабинете, в таблице «Модели и цены».\n\n## Ошибки и повторы\n\nОшибка приходит с кодом HTTP и телом, как у OpenAI: `{\"error\": {\"code\", \"message\", \"type\"}}`. Отказ шлюза — ключ, баланс, лимит — приходит до модели, ничего не стоит и помечен заголовком `X-Xwires-Refusal` с его кодом; ошибка самой модели — с её кодом и текстом, без этого заголовка. Незнакомый путь — `404 not_found`. У каждого ответа есть заголовок `X-Request-Id`: если пишете нам, приложите его.\n\n429, 500, 502, 503 и обрыв соединения повторяйте с нарастающей паузой, а если в ответе 429 есть `Retry-After`, — через столько секунд, сколько в нём. 400, 401, 402, 403 и 404 повтором не исправить.\n\n## Расход и лимиты\n\nКаждый запрос списывается с баланса компании по её ценам, а пока баланс не больше нуля, запросы получают `402 insufficient_balance`. Лимиты трат на компанию или ключ, за день и месяц по UTC, задаёт админ в кабинете: сверх них — `403 spend_limit_exceeded`. Когда у компании идёт столько запросов, сколько ей можно одновременно, следующий ждёт свободного места до 10 секунд и только потом получает `429 too_many_concurrent_requests`."
  },
  "servers": [
    {
      "url": "https://api.xwires.ai/v1",
      "description": "xWires"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Модели"
    },
    {
      "name": "Chat Completions"
    },
    {
      "name": "Responses"
    }
  ],
  "paths": {
    "/models": {
      "get": {
        "operationId": "listModels",
        "summary": "Модели ключа",
        "description": "Модели в продаже, которые открыты ключу, с контекстом, пределом ответа и возможностями. Цен в ответе нет.",
        "tags": [
          "Модели"
        ],
        "responses": {
          "200": {
            "description": "Модели ключа.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: Тело запроса — не объект JSON, или в нём то, чего шлюз не принимает.",
            "x-xwires-codes": [
              "invalid_request"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: Ключа нет, ключ неверный, заменён или отозван.\n\n`expired_api_key`: Срок ключа вышел.",
            "x-xwires-codes": [
              "invalid_api_key",
              "expired_api_key"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`account_disabled`: Шлюз для компании закрыт.",
            "x-xwires-codes": [
              "account_disabled"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`billing_unavailable`: Шлюз не может проверить баланс.",
            "x-xwires-codes": [
              "billing_unavailable"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/models/{model}": {
      "get": {
        "operationId": "retrieveModel",
        "summary": "Модель ключа по ID",
        "description": "Одна модель из `GET /models`, в том же виде. Модели, которой нет в списке ключа, нет и здесь: `404 model_not_found`.",
        "tags": [
          "Модели"
        ],
        "parameters": [
          {
            "name": "model",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID модели."
          }
        ],
        "responses": {
          "200": {
            "description": "Модель.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: Тело запроса — не объект JSON, или в нём то, чего шлюз не принимает.",
            "x-xwires-codes": [
              "invalid_request"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: Ключа нет, ключ неверный, заменён или отозван.\n\n`expired_api_key`: Срок ключа вышел.",
            "x-xwires-codes": [
              "invalid_api_key",
              "expired_api_key"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`account_disabled`: Шлюз для компании закрыт.",
            "x-xwires-codes": [
              "account_disabled"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`model_not_found`: Такой модели нет или она больше не продаётся; либо ID с провайдером, как `goat-claude/claude-opus-5-5`, — тогда новый ID в сообщении ошибки.",
            "x-xwires-codes": [
              "model_not_found"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`billing_unavailable`: Шлюз не может проверить баланс.",
            "x-xwires-codes": [
              "billing_unavailable"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/chat/completions": {
      "post": {
        "operationId": "createChatCompletion",
        "summary": "Ответ модели на диалог",
        "description": "Тот же запрос, что у OpenAI: подходят официальные SDK и готовый код.",
        "tags": [
          "Chat Completions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatCompletionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ответ модели.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletion"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "С `stream: true`: события `data:` с `ChatCompletionChunk` в JSON, последнее — `data: [DONE]`."
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: Тело запроса — не объект JSON, или в нём то, чего шлюз не принимает.\n\n`invalid_model`: Поля `model` нет, оно не строка или модель названа дважды.\n\n`unsupported_feature`: Модель не умеет того, что просит запрос, например поиска, или шлюз не пропускает встроенный инструмент.\n\nИли ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [
              "invalid_request",
              "invalid_model",
              "unsupported_feature"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: Ключа нет, ключ неверный, заменён или отозван.\n\n`expired_api_key`: Срок ключа вышел.",
            "x-xwires-codes": [
              "invalid_api_key",
              "expired_api_key"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "`insufficient_balance`: На балансе компании не осталось денег.",
            "x-xwires-codes": [
              "insufficient_balance"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`account_disabled`: Шлюз для компании закрыт.\n\n`spend_limit_exceeded`: Исчерпан лимит трат ключа или компании за день или месяц.\n\n`model_not_allowed`: Модель не открыта ключу или компании.",
            "x-xwires-codes": [
              "account_disabled",
              "spend_limit_exceeded",
              "model_not_allowed"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`model_not_found`: Такой модели нет или она больше не продаётся; либо ID с провайдером, как `goat-claude/claude-opus-5-5`, — тогда новый ID в сообщении ошибки.",
            "x-xwires-codes": [
              "model_not_found"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limit_exceeded`: Слишком много запросов или токенов в минуту на ключ.\n\n`too_many_concurrent_requests`: У компании слишком много запросов сразу, и за 10 секунд место не освободилось.",
            "x-xwires-codes": [
              "rate_limit_exceeded",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: Шлюз не смог проверить запрос.\n\nИли ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [
              "internal_error"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`billing_unavailable`: Шлюз не может проверить баланс.\n\nИли ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [
              "billing_unavailable"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/responses": {
      "post": {
        "operationId": "createResponse",
        "summary": "Ответ модели с инструментами",
        "description": "Интерфейс со встроенными инструментами на стороне сервера, главный из них — поиск в интернете. Каждый поиск списывается по цене вызова из кабинета, а его результаты входят в токены ввода. Запрос с поиском к модели без него получает `400 unsupported_feature` и ничего не стоит.",
        "tags": [
          "Responses"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResponseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ответ модели.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Response"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "С `stream: true`: события SSE Responses API OpenAI, например `response.output_text.delta` с куском текста, последнее — `response.completed` с ответом целиком."
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: Тело запроса — не объект JSON, или в нём то, чего шлюз не принимает.\n\n`invalid_model`: Поля `model` нет, оно не строка или модель названа дважды.\n\n`unsupported_feature`: Модель не умеет того, что просит запрос, например поиска, или шлюз не пропускает встроенный инструмент.\n\nИли ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [
              "invalid_request",
              "invalid_model",
              "unsupported_feature"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: Ключа нет, ключ неверный, заменён или отозван.\n\n`expired_api_key`: Срок ключа вышел.",
            "x-xwires-codes": [
              "invalid_api_key",
              "expired_api_key"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "`insufficient_balance`: На балансе компании не осталось денег.",
            "x-xwires-codes": [
              "insufficient_balance"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`account_disabled`: Шлюз для компании закрыт.\n\n`spend_limit_exceeded`: Исчерпан лимит трат ключа или компании за день или месяц.\n\n`model_not_allowed`: Модель не открыта ключу или компании.",
            "x-xwires-codes": [
              "account_disabled",
              "spend_limit_exceeded",
              "model_not_allowed"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`model_not_found`: Такой модели нет или она больше не продаётся; либо ID с провайдером, как `goat-claude/claude-opus-5-5`, — тогда новый ID в сообщении ошибки.",
            "x-xwires-codes": [
              "model_not_found"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limit_exceeded`: Слишком много запросов или токенов в минуту на ключ.\n\n`too_many_concurrent_requests`: У компании слишком много запросов сразу, и за 10 секунд место не освободилось.",
            "x-xwires-codes": [
              "rate_limit_exceeded",
              "too_many_concurrent_requests"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: Шлюз не смог проверить запрос.\n\nИли ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [
              "internal_error"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`billing_unavailable`: Шлюз не может проверить баланс.\n\nИли ошибка самой модели, с её кодом и текстом.",
            "x-xwires-codes": [
              "billing_unavailable"
            ],
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Xwires-Refusal": {
                "$ref": "#/components/headers/X-Xwires-Refusal"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sk-xw- и 64 шестнадцатеричных символа",
        "description": "Ключ команды: Кабинет компании → API к моделям."
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "id запроса: если пишете нам, приложите его. По нему запрос находится в «Расходе токенов» в кабинете.",
        "schema": {
          "type": "string"
        }
      },
      "Retry-After": {
        "description": "Через сколько секунд повторить.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Xwires-Refusal": {
        "description": "Код отказа шлюза: модель не спрашивали, запрос ничего не стоит. У ошибки модели заголовка нет, даже с таким же кодом.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "ModelList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          }
        }
      },
      "Model": {
        "type": "object",
        "required": [
          "id",
          "object",
          "created",
          "owned_by",
          "context_length",
          "max_output_tokens"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "ID модели: его ставят в поле `model` запроса."
          },
          "object": {
            "type": "string",
            "enum": [
              "model"
            ]
          },
          "created": {
            "type": "integer",
            "description": "Всегда 0."
          },
          "owned_by": {
            "type": "string",
            "description": "Поле формата OpenAI. Модель выбирается по `id`."
          },
          "context_length": {
            "type": "integer",
            "description": "Контекст модели в токенах."
          },
          "max_output_tokens": {
            "type": "integer",
            "description": "Предел токенов ответа вместе с рассуждением."
          },
          "capabilities": {
            "type": "object",
            "additionalProperties": true,
            "description": "Что умеет модель, как её описал xWires, например `{\"tools\": true, \"vision\": false}`. Может не быть."
          }
        }
      },
      "ChatCompletionRequest": {
        "type": "object",
        "required": [
          "model",
          "messages"
        ],
        "description": "Запрос Chat Completions, как в API OpenAI. Описаны главные поля, остальные — в документации OpenAI.",
        "properties": {
          "model": {
            "type": "string",
            "description": "ID модели из кабинета или `GET /models`."
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "description": "Диалог по порядку.",
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "stream": {
            "type": "boolean",
            "default": false,
            "description": "`true` — ответ приходит по частям, событиями SSE, по мере того как модель его пишет."
          },
          "stream_options": {
            "type": "object",
            "properties": {
              "include_usage": {
                "type": "boolean",
                "description": "`true` — `usage` придёт в последнем куске, без него — не придёт. Этот кусок может нести и `choices` с `finish_reason`: ищите `usage` в куске, где оно не `null`, а не по пустому `choices`."
              }
            }
          },
          "temperature": {
            "type": "number",
            "description": "Случайность ответа, обычно от 0 до 1: чем выше, тем разнообразнее ответ, чем ниже, тем предсказуемее."
          },
          "max_tokens": {
            "type": "integer",
            "minimum": 1,
            "description": "Предел токенов ответа вместе с рассуждением модели: слишком малый оставит ответ пустым с `finish_reason: length`. Больше предела модели не выйдет."
          },
          "tools": {
            "type": "array",
            "description": "Функции, которые модель может вызвать, как в API OpenAI. Выполняет их ваш код.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "ChatMessage": {
        "type": "object",
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "type": "string",
            "description": "`system`, `user`, `assistant` или `tool`."
          },
          "content": {
            "description": "Текст или список блоков, как в API OpenAI.",
            "oneOf": [
              {
                "type": "string",
                "nullable": true
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            ]
          }
        }
      },
      "ChatCompletion": {
        "type": "object",
        "required": [
          "id",
          "object",
          "created",
          "model",
          "choices"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "chat.completion"
            ]
          },
          "created": {
            "type": "integer",
            "description": "Время ответа в секундах Unix."
          },
          "model": {
            "type": "string",
            "description": "Модель, которая ответила."
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "index",
                "message",
                "finish_reason"
              ],
              "properties": {
                "index": {
                  "type": "integer"
                },
                "message": {
                  "type": "object",
                  "properties": {
                    "role": {
                      "type": "string",
                      "enum": [
                        "assistant"
                      ]
                    },
                    "content": {
                      "type": "string",
                      "nullable": true,
                      "description": "Текст ответа."
                    },
                    "reasoning": {
                      "type": "string",
                      "description": "Рассуждение модели перед ответом."
                    },
                    "reasoning_content": {
                      "type": "string",
                      "description": "Рассуждение у части моделей приходит здесь, а не в `reasoning`."
                    },
                    "tool_calls": {
                      "type": "array",
                      "description": "Вызовы функций из `tools`.",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "finish_reason": {
                  "type": "string",
                  "description": "Почему модель остановилась: `stop` — ответ закончен, `length` — ответ с рассуждением дошёл до `max_tokens`, `tool_calls` — модель вызывает функцию."
                }
              }
            }
          },
          "usage": {
            "$ref": "#/components/schemas/CompletionUsage"
          }
        }
      },
      "ChatCompletionChunk": {
        "type": "object",
        "description": "Кусок ответа по частям: сначала идут куски с `delta.reasoning`, потом с текстом в `delta.content`. Последний кусок несёт `finish_reason` и, если его просили в `stream_options`, `usage`.",
        "required": [
          "id",
          "object",
          "created",
          "model",
          "choices"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "chat.completion.chunk"
            ]
          },
          "created": {
            "type": "integer"
          },
          "model": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "delta": {
                  "type": "object",
                  "properties": {
                    "role": {
                      "type": "string"
                    },
                    "content": {
                      "type": "string",
                      "nullable": true
                    },
                    "reasoning": {
                      "type": "string"
                    },
                    "reasoning_content": {
                      "type": "string"
                    },
                    "tool_calls": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "finish_reason": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "usage": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Токены запроса, как `usage` в `ChatCompletion`: только в последнем куске."
          }
        }
      },
      "CompletionUsage": {
        "type": "object",
        "description": "Токены запроса: по ним он списывается с баланса компании.",
        "required": [
          "prompt_tokens",
          "completion_tokens",
          "total_tokens"
        ],
        "properties": {
          "prompt_tokens": {
            "type": "integer",
            "description": "Токены запроса."
          },
          "completion_tokens": {
            "type": "integer",
            "description": "Токены ответа вместе с рассуждением."
          },
          "total_tokens": {
            "type": "integer",
            "description": "Всего."
          },
          "prompt_tokens_details": {
            "type": "object",
            "properties": {
              "cached_tokens": {
                "type": "integer",
                "description": "Сколько из них взято из кэша: они дешевле нового ввода."
              }
            }
          },
          "completion_tokens_details": {
            "type": "object",
            "properties": {
              "reasoning_tokens": {
                "type": "integer",
                "description": "Сколько из них ушло на рассуждение."
              }
            }
          }
        }
      },
      "ResponseRequest": {
        "type": "object",
        "required": [
          "model",
          "input"
        ],
        "description": "Запрос Responses, как в API OpenAI. Описаны главные поля, остальные — в документации OpenAI.",
        "properties": {
          "model": {
            "type": "string",
            "description": "ID модели из кабинета или `GET /models`."
          },
          "input": {
            "description": "Текст или сообщения с `role` и `content`. `content` — строка или список блоков, например `{\"type\": \"input_text\", \"text\": \"…\"}`.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            ]
          },
          "tools": {
            "type": "array",
            "description": "Инструменты. `{\"type\": \"web_search\"}` включает поиск в интернете — у моделей, где в кабинете есть его цена. Из встроенных инструментов шлюз пропускает только поиск, свои функции — как в API OpenAI, кроме загружаемых по требованию (`defer_loading`).",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "max_tool_calls": {
            "type": "integer",
            "minimum": 1,
            "description": "Сколько поисков модель может сделать за запрос. Больше предела модели не выйдет: шлюз ставит его сам."
          },
          "max_output_tokens": {
            "type": "integer",
            "minimum": 1,
            "description": "Предел токенов ответа вместе с рассуждением. Больше предела модели не выйдет."
          },
          "stream": {
            "type": "boolean",
            "default": false,
            "description": "`true` — ответ по частям, событиями SSE."
          }
        }
      },
      "Response": {
        "type": "object",
        "required": [
          "id",
          "object",
          "model",
          "output"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "response"
            ]
          },
          "created_at": {
            "type": "integer",
            "description": "Время ответа в секундах Unix."
          },
          "model": {
            "type": "string",
            "description": "Модель, которая ответила."
          },
          "status": {
            "type": "string",
            "description": "`completed`, если ответ закончен."
          },
          "output": {
            "type": "array",
            "description": "Элементы ответа по порядку: рассуждение (`reasoning`), поиски (`web_search_call`), вызовы функций (`function_call`) и сообщение (`message`) с блоком `output_text`.",
            "items": {
              "type": "object",
              "required": [
                "type"
              ],
              "properties": {
                "type": {
                  "type": "string"
                }
              },
              "additionalProperties": true
            }
          },
          "usage": {
            "$ref": "#/components/schemas/ResponseUsage"
          }
        }
      },
      "ResponseUsage": {
        "type": "object",
        "description": "Токены запроса: по ним он списывается с баланса компании, а поиски — по цене вызова.",
        "required": [
          "input_tokens",
          "output_tokens",
          "total_tokens"
        ],
        "properties": {
          "input_tokens": {
            "type": "integer",
            "description": "Токены запроса вместе с результатами поиска."
          },
          "output_tokens": {
            "type": "integer",
            "description": "Токены ответа вместе с рассуждением."
          },
          "total_tokens": {
            "type": "integer",
            "description": "Всего."
          },
          "input_tokens_details": {
            "type": "object",
            "properties": {
              "cached_tokens": {
                "type": "integer",
                "description": "Сколько из них взято из кэша."
              }
            }
          },
          "output_tokens_details": {
            "type": "object",
            "properties": {
              "reasoning_tokens": {
                "type": "integer",
                "description": "Сколько из них ушло на рассуждение."
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "nullable": true,
                "description": "Код отказа шлюза или код ошибки модели. Решения принимайте по коду HTTP и этому коду.",
                "x-extensible-enum": [
                  "invalid_request",
                  "invalid_model",
                  "unsupported_feature",
                  "invalid_api_key",
                  "expired_api_key",
                  "insufficient_balance",
                  "spend_limit_exceeded",
                  "model_not_allowed",
                  "account_disabled",
                  "model_not_found",
                  "not_found",
                  "rate_limit_exceeded",
                  "too_many_concurrent_requests",
                  "internal_error",
                  "billing_unavailable"
                ]
              },
              "message": {
                "type": "string",
                "description": "Что случилось, по-английски."
              },
              "param": {
                "type": "string",
                "description": "Поле запроса, о котором ошибка, если модель его назвала."
              },
              "type": {
                "type": "string",
                "description": "Вид ошибки, как у OpenAI: `invalid_request_error`, у 5xx — `server_error`. У ошибки модели — её собственный, если она его дала."
              }
            }
          }
        }
      }
    }
  }
}
