# Ответ чат-модели

`POST https://api.souz.ai/v1/chat/completions`

Контракт OpenAI Chat Completions. Поля, которых нет в схеме, передаются модели как есть;
что модель читает — её карточка (`params`, `capabilities`). С `"stream": true` ответ —
`text/event-stream`: чанки `chat.completion.chunk`, у каждого есть `choices[0]`; с
`stream_options.include_usage` последний чанк — пустой `choices` и `usage` с `cost`; в
конце `data: [DONE]`.

Если исход вызова установить нельзя (модель не ответила вовремя, поток оборвался до
итога), ответ — `502 generation_failed`, ничего не списано. Неизвестная ошибка —
`502 unknown_error`, тоже без списания. Обрыв соединения с вашей
стороны вызов не отменяет: ответ дочитывается и списывается по факту — итог виден в
`GET /v1/jobs/{id}` по `id` ответа. Повтор с тем же `Idempotency-Key` и тем же телом
дожидается первого вызова и получает его ответ без второго списания; тот же ключ с
другим телом — `409`.

## Авторизация

- `BearerAuth`: API-ключ из кабинета souz.ai — запускает модели. Ключ управления здесь не подходит: `403 forbidden` с `detail[].reason: requires_api_key`.

## Параметры

| Имя | Где | Обязательно | Тип | Описание |
|---|---|---|---|---|
| `Idempotency-Key` | header | нет | `string` | Строка до 255 символов, уникальная для аккаунта. Повтор с тем же ключом и параметрами возвращает ту же задачу с текущим статусом без новой задачи и повторного списания, в том числе при одновременных запросах, даже если настройки аккаунта за это время изменились: сравниваются только поля запроса. Другие параметры — `409 idempotency_key_conflict`; store и callback_url в сравнение не входят. У изображений, видео и музыки также не сравнивается response_format; у синтеза речи формат звука сравнивается. Ключ хранится вместе с задачей, сейчас без ограничения срока. Повтор задачи со статусом failed не запускает её заново: для новой попытки нужен новый ключ. Срок хранения файлов результата — `retention.files_days` из `GET /v1/key`; повтор его не продлевает. Поддерживается для генерации изображений, видео и музыки, транскрибации, синтеза речи и чата. У чата сравнивается тело запроса целиком; повтор дожидается идущего вызова и получает тот же ответ (и поток), что первый; ответ хранится сутки. |

## Тело запроса (`application/json`)

Схема `ChatCompletionRequest`.

| Поле | Обязательно | Тип | Описание |
|---|---|---|---|
| `max_completion_tokens` | нет | `integer` |  |
| `max_price_multiplier` | нет |  |  |
| `max_tokens` | нет | `integer` | Граница ответа (или `max_completion_tokens`). Без поля — предел модели. Если баланса хватает не на весь ответ, модели передаётся граница по оплачиваемому (не меньше 1000 токенов). |
| `messages` | да | `array` |  |
| `model` | да | `string` |  |
| `n` | нет | `integer` |  |
| `reasoning_effort` | нет | `string` |  |
| `response_format` | нет | `object` | `{"type": "json_schema", …}` — у моделей с `capabilities.structured_output`. |
| `routing` | нет | схема `Routing` |  |
| `routing_options` | нет |  |  |
| `store` | нет | `boolean | null` | `false` — запрос без хранения: тексты запроса и ответа не сохраняются; файлы живут обычный срок. По умолчанию тексты хранятся; `true` не отменяет режим без хранения аккаунта. |
| `stream` | нет | `boolean`; по умолчанию `false` |  |
| `temperature` | нет | `number, double` |  |
| `tool_choice` | нет |  | `auto`, `none`, `required` или конкретная функция. `required` и конкретную функцию исполняют не все каналы; если ни один — `400 capability_mismatch` с путём `tool_choice`. |
| `tools` | нет | `array` | Инструменты OpenAI; модель без `capabilities.tools` — `400 capability_mismatch`. |

## Пример запроса

```bash
curl -X POST 'https://api.souz.ai/v1/chat/completions' \
  -H "Authorization: Bearer $SOUZ_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
  "messages": [
    {
      "content": "Привет!",
      "role": "user"
    }
  ],
  "model": "gpt-5-nano"
}'
```

```python
import os
import requests

response = requests.post(
    "https://api.souz.ai/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['SOUZ_API_KEY']}"},
    json={
        "messages": [
            {
                "content": "Привет!",
                "role": "user",
            },
        ],
        "model": "gpt-5-nano",
    },
)
print(response.json())
```

```ts
const response = await fetch("https://api.souz.ai/v1/chat/completions", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SOUZ_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "messages": [
      {
        "content": "Привет!",
        "role": "user"
      }
    ],
    "model": "gpt-5-nano"
  }),
});
console.log(await response.json());
```

## Ответы

### 200

Ответ модели целиком или потоком.

- `application/json`, схема `ChatCompletion`

```json
{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "Привет! Чем помочь?",
        "role": "assistant"
      }
    }
  ],
  "created": 1758801600,
  "id": "chatcmpl-1a2b3c4d5e6f",
  "model": "gpt-5-nano",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 24,
    "cost": 3000,
    "currency": "RUB",
    "prompt_tokens": 10,
    "total_tokens": 34
  }
}
```
- `text/event-stream`, схема `ChatCompletionChunk`

### 400

Запрос отклонён; `error.detail[]` называет поля и допустимые значения.

- `application/json`, схема `Error`

```json
{
  "error": {
    "code": "capability_mismatch",
    "detail": [
      {
        "allowed": [
          "1:1",
          "16:9",
          "9:16"
        ],
        "path": "aspect_ratio",
        "reason": "unsupported_value"
      }
    ],
    "message": "the model does not support this value",
    "type": "invalid_request_error"
  }
}
```

### 401

Нет ключа или ключ недействителен.

- `application/json`, схема `Error`

### 402

Баланса не хватает на самый дешёвый канал с учётом запросов, которые ещё выполняются (`insufficient_balance`), или исчерпан лимит трат ключа (`key_spend_limit_exceeded`) либо участника, которому он выдан (`member_spend_limit_exceeded`). Повтор не поможет, пока баланс не пополнят, а лимит не начнётся заново или его не поднимут: потолок ключа со `spend_limit_reset: none` — на всё время ключа.

- `application/json`, схема `Error`

```json
{
  "error": {
    "code": "insufficient_balance",
    "message": "insufficient balance",
    "type": "billing_error"
  }
}
```

### 403

Нельзя: у ключа не тот вид (модели запускает API-ключ, аккаунт — ключ управления), у роли участника нет права или подпись ссылки на файл не сходится. `detail[].reason` говорит, что именно: `requires_api_key`, `requires_management_key`, `cabinet_only` (только в кабинете), `insufficient_role` (в `allowed` — роли, которым можно), `not_key_author` (секрет ключа открывает только тот, кто его создал).

- `application/json`, схема `Error`

```json
{
  "error": {
    "code": "forbidden",
    "detail": [
      {
        "reason": "requires_api_key"
      }
    ],
    "message": "this endpoint takes an API key; a management key manages the account only",
    "type": "permission_error"
  }
}
```

### 404

Нет такого объекта у этого аккаунта.

- `application/json`, схема `Error`

### 409

Тот же `Idempotency-Key` с другим телом.

- `application/json`, схема `Error`

### 413

Тело или файл больше предела (`invalid_input`, `detail[].reason: too_large`) или — только у `POST /v1/files` — загрузки аккаунта заняли квоту (`storage_limit_exceeded`).

- `application/json`, схема `Error`

### 415

Тип тела не тот, что принимает метод: `Content-Type: application/json` (у загрузки файла и транскрибации — `multipart/form-data`). В `detail[]` — `{"path": "body", "reason": "unsupported_format"}` и принимаемые типы.

- `application/json`, схема `Error`

### 429

Слишком часто — повторите через `Retry-After` секунд. Запуск моделей, задачи и файлы результатов частотой не ограничены (их ограничивают баланс и лимиты трат); здесь 429 — на поток запросов с ключом, который сервер не знает, с одного адреса, и на потоки сверх 1000 одновременных на аккаунт (события задачи, чат с `stream: true`). Пределы — раздел «Лимиты» руководства.

- `application/json`, схема `Error`

### 502

Модель не справилась или исход вызова неизвестен; ничего не списано.

- `application/json`, схема `Error`

### 503

Модель сейчас некому исполнить — повторите через `Retry-After`. На плановых работах — `maintenance` (ответ `Maintenance`).

- `application/json`, схема `Error`

---

Оглавление документации: https://souz.ai/llms.txt. Всё руководство одним файлом: https://souz.ai/llms-full.txt. Справочник методов: https://souz.ai/docs/api-reference.md.
