> Для ИИ-агентов: пошаговая инструкция по настройке — [`/docs/agents.md`](https://gate.joingonka.ai/docs/agents.md), индекс документации — [`/llms.txt`](https://gate.joingonka.ai/llms.txt).

# Справочник API

Всё о запросах к шлюзу: протоколы, адреса, ключи и параметры. Ниже — стриминг, вызов инструментов, рассуждения, плагины и стоимость запроса в ответе.

## Протоколы и адреса

Шлюз принимает форматы OpenAI и Anthropic. Базовый адрес для SDK OpenAI — `https://gate.joingonka.ai/v1`, для SDK Anthropic — `https://gate.joingonka.ai`. Все протоколы работают на одном ключе и одном балансе: запрос в любом формате идёт одним и тем же путём.

| Метод и путь | Формат | Для чего | Особенности |
| --- | --- | --- | --- |
| `POST /v1/chat/completions` | OpenAI Chat Completions | Чат, агенты, вызов инструментов | Основной путь: остальные форматы шлюз переводит в него. |
| `POST /v1/messages` | Anthropic Messages | Claude Code и SDK Anthropic | Базовый адрес без `/v1`; модели `claude-*` заменяются рекомендуемой; поле `max_tokens` обязательно. |
| `POST /v1/responses` | OpenAI Responses | Codex CLI и новые SDK OpenAI | Без состояния: историю присылайте целиком в каждом запросе. |
| `POST /v1/completions` | OpenAI Completions (legacy) | Автодополнение и правка кода в редакторах | `suffix` передаётся модели подсказкой; в ответе `logprobs: null`. |
| `POST /v1/embeddings` | OpenAI Embeddings | Векторные представления текста | Ответ `501`: в сети нет эмбеддинговых моделей. |

### Справочные адреса

Отвечают без ключа. Таблица моделей с контекстом и статусом — в разделе [Модели](https://gate.joingonka.ai/ru/docs/models).

| Метод и путь | Описание |
| --- | --- |
| `GET /v1/models` | Список моделей: контекст, цены, поддерживаемые параметры — поля в формате OpenRouter. |
| `GET /v1/models/{model}` | Карточка одной модели; слэш в id — как есть или `%2F`. Скрытая или неизвестная модель — `404 model_not_found`. |
| `GET /v1/capabilities` | Возможности шлюза: параметры, протоколы, плагины, поля стоимости и лимиты (`limits`). |
| `GET /v1/plugins` | Плагины: id и название. |
| `GET /v1/network-status` | Состояние моделей сети: доступность, задержки, аптайм. |
| `GET /v1/nodes` | Сводка пула нод: сколько всего, активных и в карантине. |
| `GET /v1/web-search/engines` | Включён ли веб-поиск и в каком состоянии его движки. |

### Anthropic Messages

- Базовый адрес — `https://gate.joingonka.ai`: SDK сам добавит `/v1/messages`.
- Ключ — в заголовке `x-api-key` (так шлёт SDK Anthropic) или `Authorization: Bearer`.
- Модели `claude-*` шлюз заменяет рекомендуемой (`MiniMaxAI/MiniMax-M2.7`); в поле `model` ответа остаётся имя, которое прислал клиент.
- `max_tokens` обязателен, как в API Anthropic; больше потолка модели — срезается.
- Стрим — события Anthropic; в паузах шлюз шлёт `event: ping`, сбой приходит событием `event: error`.
- Рассуждения модели в ответ не попадают: блоков `thinking` нет.
- Встроенный инструмент `web_search` исполняет плагин веб-поиска шлюза — см. раздел [Плагины](https://gate.joingonka.ai/ru/docs/api#plugins).
- Подсчёта токенов (`/v1/messages/count_tokens`) нет — ответ `404`.

Claude Code проще настроить установщиком — [подключение инструментов](https://gate.joingonka.ai/ru/docs#connect). Вручную — переменными окружения; `ANTHROPIC_MODEL` закрепляет модель сети.

#### Claude Code

```bash
export ANTHROPIC_BASE_URL=https://gate.joingonka.ai
export ANTHROPIC_AUTH_TOKEN=$JOINGONKA_API_KEY
export ANTHROPIC_MODEL=MiniMaxAI/MiniMax-M2.7
claude
```

#### cURL

```bash
curl https://gate.joingonka.ai/v1/messages \
  -H "x-api-key: $JOINGONKA_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMaxAI/MiniMax-M2.7",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "What is Gonka?"}]
  }'
```

### OpenAI Responses

- Шлюз не хранит ответы: присылайте всю историю в `input`. Поля `previous_response_id` и `conversation` — ошибка `400` с кодом.
- `store` принимается и ничего не меняет.
- Инструменты: `function` и `web_search` — его исполняет плагин веб-поиска. Другие встроенные инструменты шлюз пропускает, и запрос выполняется без них; требовать такой инструмент через `tool_choice` — ошибка `400`.
- Части `input_image` и `input_file` — ошибка `400`: модели сети работают с текстом.
- Адреса состояния (`GET /v1/responses/{id}`, `DELETE /v1/responses/{id}`, `GET /v1/responses/{id}/input_items`, `POST /v1/responses/{id}/cancel`, `POST /v1/responses/compact`) отвечают `404` с кодом — ответов шлюз не хранит.
- Codex CLI: задайте свой id провайдера в `model_provider` (не openai) — тогда Codex сжимает историю сам, без `/v1/responses/compact`.

### Legacy Completions

- `prompt` — строка или массив из одной строки; ответ — в `choices[].text`. Несколько промптов или токены вместо текста — ошибка `400`.
- `suffix` передаётся модели подсказкой в промпте: настоящего заполнения середины у сети нет.
- В ответе `logprobs: null`; `best_of` игнорируется; `echo` работает.

## Ключи и авторизация

Ключ передаётся в заголовке `Authorization: Bearer jg-…` или `x-api-key: jg-…` — на всех адресах. Ключ создаётся после регистрации на странице [gate.joingonka.ai/keys](https://gate.joingonka.ai/keys).

| Префикс | Ключ | Запросы к моделям |
| --- | --- | --- |
| `jg-` | Обычный ключ аккаунта | да |
| `gc-` | Дочерний ключ: свои лимиты, расход — с баланса владельца | да |
| `gm-` | Управляющий ключ: только управление дочерними | нет — `403 forbidden` |

- Каждому ключу в кабинете можно задать лимит расходов на день, месяц и всего; превышение — `402 child_key_limit_exceeded`.
- Число запросов в минуту на ключ ограничено — значения в разделе [Лимиты](https://gate.joingonka.ai/ru/docs/errors#limits).
- Без ключа работает только демо-чат на сайте: запрос без ключа из своего кода получит `402` с `is_demo`.
- Ключами управляют только в кабинете: `/api/keys` с API-ключом недоступен. Баланс и расход по ключу — [API аккаунта](https://gate.joingonka.ai/ru/docs/billing#account-api).

> Ключ — секрет: не храните его в репозитории и в коде фронтенда, передавайте через переменные окружения.

## Примеры

Один и тот же запрос в четырёх SDK. Модель — рекомендуемая (`MiniMaxAI/MiniMax-M2.7`), ключ — из переменной окружения `JOINGONKA_API_KEY`.

### Python

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://gate.joingonka.ai/v1",
    api_key=os.environ["JOINGONKA_API_KEY"],
)

response = client.chat.completions.create(
    model="MiniMaxAI/MiniMax-M2.7",
    messages=[{"role": "user", "content": "What is Gonka?"}],
)
print(response.choices[0].message.content)
```

### TypeScript

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://gate.joingonka.ai/v1",
  apiKey: process.env.JOINGONKA_API_KEY,
});

const response = await client.chat.completions.create({
  model: "MiniMaxAI/MiniMax-M2.7",
  messages: [{ role: "user", content: "What is Gonka?" }],
});
console.log(response.choices[0].message.content);
```

### cURL

```bash
curl https://gate.joingonka.ai/v1/chat/completions \
  -H "Authorization: Bearer $JOINGONKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMaxAI/MiniMax-M2.7",
    "messages": [{"role": "user", "content": "What is Gonka?"}]
  }'
```

### Anthropic SDK

```python
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://gate.joingonka.ai",
    api_key=os.environ["JOINGONKA_API_KEY"],
)

message = client.messages.create(
    model="MiniMaxAI/MiniMax-M2.7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "What is Gonka?"}],
)
print(message.content[0].text)
```

### Потоковый ответ

#### Python

```python
import os
from openai import OpenAI

client = OpenAI(base_url="https://gate.joingonka.ai/v1", api_key=os.environ["JOINGONKA_API_KEY"])

stream = client.chat.completions.create(
    model="MiniMaxAI/MiniMax-M2.7",
    messages=[{"role": "user", "content": "What is Gonka?"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

#### TypeScript

```typescript
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://gate.joingonka.ai/v1", apiKey: process.env.JOINGONKA_API_KEY });

const stream = await client.chat.completions.create({
  model: "MiniMaxAI/MiniMax-M2.7",
  messages: [{ role: "user", content: "What is Gonka?" }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```

#### cURL

```bash
curl -N https://gate.joingonka.ai/v1/chat/completions \
  -H "Authorization: Bearer $JOINGONKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMaxAI/MiniMax-M2.7",
    "messages": [{"role": "user", "content": "What is Gonka?"}],
    "stream": true
  }'
```

#### Anthropic SDK

```python
import os
import anthropic

client = anthropic.Anthropic(base_url="https://gate.joingonka.ai", api_key=os.environ["JOINGONKA_API_KEY"])

with client.messages.stream(
    model="MiniMaxAI/MiniMax-M2.7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "What is Gonka?"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

## Параметры запроса

Параметры `POST /v1/chat/completions`, которые шлюз гарантирует сам, — список `supported_parameters` в ответе возможностей:

| Параметр | Описание |
| --- | --- |
| `temperature` | Случайность ответа: чем выше, тем разнообразнее. |
| `top_p` | Отбор токенов по суммарной вероятности. |
| `top_k` | Отбор из k самых вероятных токенов. |
| `min_p` | Отсечка маловероятных токенов относительно самого вероятного. |
| `frequency_penalty` | Штраф за частые повторы. |
| `presence_penalty` | Штраф за уже встречавшиеся токены. |
| `repetition_penalty` | Множитель против повторов. |
| `stop` | Строки, на которых генерация останавливается. |
| `seed` | Зерно для воспроизводимости. |
| `max_tokens` | Лимит токенов ответа; больше потолка модели — срезается до потолка. |
| `max_completion_tokens` | Другое имя `max_tokens`: шлюз переносит значение в него. |
| `tools` | Функции, которые модель может вызвать, в формате OpenAI. |
| `tool_choice` | Вызывать ли функцию: на выбор модели, никогда, обязательно или конкретную. |
| `response_format` | Структурированный ответ: `json_object` или `json_schema`. |

- Без `temperature` шлюз подставляет `0.7`.
- Без `max_tokens` шлюз подставляет умолчание модели: без стрима — короче, в стриме — потолок модели. Числа по моделям — в разделе [Лимиты](https://gate.joingonka.ai/ru/docs/errors#limits).

### Передаются в сеть как есть

`reasoning_effort`, `reasoning`, `enable_thinking`, `chat_template_kwargs`, `thinking_token_budget`, `min_tokens`, `logit_bias`, `n`, `parallel_tool_calls`, `extra_body`. Шлюз их не проверяет: значение вне списка сети — ошибка `400` с типом `api_error`.

### Не передаются в сеть

Остальные поля шлюз принимает и не передаёт в сеть — например, `user`, `metadata`, `store`, `logprobs`, `top_logprobs`, `thinking`, `stream_options`, `web_search_options`. `usage` в стриме приходит всегда.

## Стриминг

- `stream: true` — ответ событиями SSE; последнее событие — `data: [DONE]`.
- Перед завершением приходит чанк с `usage` — всегда, даже без `stream_options`.
- В паузах шлюз каждые 15 с шлёт комментарий `: keep-alive` — SSE-клиенты его пропускают.
- Пока поток не открыт, отказ приходит обычным кодом ответа; после открытия — чанком `joingonka-error`.
- В `delta.tool_calls` — один вызов на чанк: склеенные сетью вызовы шлюз разрезает.

```text
data: {"object":"chat.completion.chunk","model":"MiniMaxAI/MiniMax-M2.7","choices":[{"index":0,"delta":{"content":"Hi"},"finish_reason":null}]}

data: {"object":"chat.completion.chunk","model":"MiniMaxAI/MiniMax-M2.7","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"object":"chat.completion.chunk","model":"MiniMaxAI/MiniMax-M2.7","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":2,"total_tokens":14}}

data: [DONE]
```

### Служебные чанки шлюза

Их можно узнать по полю `id`:

| `id` | Когда и что внутри |
| --- | --- |
| `joingonka-error` | Сбой после открытия потока: поле `error`, затем обрыв без `[DONE]`. |
| `joingonka-stream-stalled` | Сеть молчала дольше допустимой паузы: поток закрывается с `finish_reason: stop`. |
| `joingonka-stream-unfinished` | Сеть оборвала генерацию: `finish_reason: length` — продолжите следующим запросом. |
| `joingonka-citations` | Источники веб-поиска в `delta.annotations` — перед завершением. |
| `joingonka-meta` | Стоимость и тайминги — только с заголовком `x-joingonka-meta: 1`. |

### Стрим в других протоколах

- Anthropic Messages: события от `message_start` до `message_stop`, в паузах `event: ping`, сбой — `event: error`.
- OpenAI Responses: события `response.*`, сбой — `response.failed`.
- Legacy Completions: при сбое — `data: {"error": …}`, затем `[DONE]`.

## Вызов инструментов

- Формат OpenAI: `tools` и `tool_choice`. Старый формат `functions` и `function_call` тоже принимается — ответ придёт в нём же.
- В стриме — один вызов на чанк: клиенты, которые читают только первый элемент, не теряют вызовы.
- Историю, на которой сеть ответила бы ошибкой `400`, шлюз чинит: роль `developer` становится `system`, пустые и повторяющиеся id вызовов получают уникальные, `arguments` объектом превращается в строку JSON, пропущенный `type` дополняется, вызов без имени убирается вместе с результатом.
- Вызов, который модель написала разметкой в тексте, шлюз переносит в `tool_calls`; ложные вызовы в ответе на запрос без инструментов убирает.
- Генерация оборвалась посреди аргументов — придёт `finish_reason: length`, а не `tool_calls`: увеличьте лимит ответа.

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "What is the weather in Paris?"}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Current weather for a city",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  }]
}
```

### Ограничения JSON-Schema

Схемы инструментов и `response_format` сеть компилирует в грамматику; регулярные выражения — движком RE2. Шлюз приводит схему к форме, которую сеть примет:

- `$ref` раскрываются на месте, разделы `$defs` и `definitions` удаляются; рекурсивная ссылка становится схемой без ограничений.
- `pattern` с конструкциями, которых нет в RE2 (просмотр вперёд и назад, обратные ссылки, атомарные группы, сверхжадные квантификаторы), снимается; повторы больше 1000 сокращаются до 1000.
- `anyOf` и `oneOf` из констант сворачиваются в `enum`; если несворачиваемых веток больше 16, объединение снимается.

> Схема может стать мягче исходной — проверяйте аргументы вызова на своей стороне.

## Структурированный ответ

`response_format`: `{"type": "json_object"}` — ответ валидным JSON, `{"type": "json_schema", "json_schema": {"name": …, "schema": …}}` — по вашей схеме с ограничениями выше. Обрезанный JSON без стрима чинит плагин `response-healing`.

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "Name three planets."}],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "planets",
      "schema": {
        "type": "object",
        "properties": {"planets": {"type": "array", "items": {"type": "string"}}},
        "required": ["planets"]
      }
    }
  }
}
```

## Рассуждения

- Рассуждения модели приходят отдельно от ответа: `message.reasoning_content`, в стриме — `delta.reasoning_content`. Поле `reasoning` шлюз переименовывает в этот формат.
- Рассуждения тратят `max_tokens`: при маленьком лимите ответ обрывается (`finish_reason: length`) ещё до текста.
- Если текста ответа нет, а рассуждения есть, шлюз переносит их в `content` — кроме ответов с вызовом инструмента.
- `reasoning_effort` и `reasoning.effort` передаются в сеть. Если у модели только два режима, шлюз приводит значение к ним: `none` и `minimal` — к `low`, более высокие — к рассуждению по умолчанию.
- Если нода отвергла значение, шлюз понижает его (`max` и `xhigh` → `high`, `minimal` → `low`, иначе снимает поле) и повторяет запрос.
- В `/v1/messages` рассуждения не передаются — блоков `thinking` нет.

## Плагины

Плагины включаются полем `plugins` — массивом строк или объектов с опциями. Список — `GET /v1/plugins`.

| Плагин | Описание | Условия |
| --- | --- | --- |
| `response-healing` | Чинит обрезанный JSON в ответе модели. | Только без стрима и если ответ начинается с `{` или `[`. |
| `privacy-sanitization` | Маскирует в текстовых сообщениях email, IPv4, номера карт, JWT, 64-символьные hex-ключи и ключи вида `sk-…`, `gw_…`, `gm-…`, `Bearer …`. | Режим — поле `privacy_mode`: `redact` (по умолчанию) или `tokenize`. |
| `file-parser` | Извлекает текст из PDF. | Если текст сообщения целиком — PDF в base64: `data:application/pdf;base64,…` или без префикса. |
| `web` | Веб-поиск: результаты подмешиваются в запрос, ответ получает ссылки на источники. | Вместе с `privacy-sanitization` — ошибка `400`. |

### Веб-поиск

- Опции: `max_results` — от 1 до 10, по умолчанию 5; `engine` — подсказка движка; `search_prompt` — свой текст перед результатами; `enabled: false` — выключить поиск.
- Источники — в `message.annotations[].url_citation`; в стриме — чанком `joingonka-citations` перед завершением.
- `mode: "agent"` — модель сама решает, искать ли и что; `max_searches` — от 1 до 5, по умолчанию 3.
- Оплата: в обычном режиме — только токены (результаты поиска входят во входные токены); в режиме агента — токены всех шагов плюс 1000 nGNK за каждый выполненный поиск (`x_joingonka.web_search_surcharge_ngonka`).
- В Anthropic Messages и OpenAI Responses встроенный инструмент `web_search` исполняет этот же плагин в режиме агента.

#### plugins: web

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "What is new in the Gonka network?"}],
  "plugins": [{"id": "web", "max_results": 5}]
}
```

#### mode: agent

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "What is new in the Gonka network?"}],
  "plugins": [{"id": "web", "mode": "agent", "max_searches": 3}]
}
```

## Стоимость и служебные поля

Ответ без стрима несёт стоимость запроса в `usage`:

| Поле | Описание |
| --- | --- |
| `usage.cost_gnk` | Стоимость запроса в GNK |
| `usage.platform_fee_gnk` | Из неё — наценка платформы, GNK |
| `usage.total_cost_gnk` | Итог к списанию в GNK |
| `usage.total_cost_usd` | Итог в долларах по текущему курсу GNK |

- В стриме `usage` содержит только токены; стоимость — в чанке `joingonka-meta`.
- С заголовком `x-joingonka-meta: 1` ответ `POST /v1/chat/completions` получает блок `x_joingonka`: стоимость (`cost_ngonka`), баланс после списания (`balance_ngonka`, только без стрима) и тайминги (`ttft_ms`). Другие протоколы этот блок не отдают.
- `x-request-id` — идентификатор запроса: приложите его к обращению в поддержку.
- `Retry-After` приходит с `429`: столько секунд подождать перед повтором.
- Заголовки `X-Title` и `HTTP-Referer` (как у OpenRouter) помогают шлюзу узнать ваше приложение; их текст не сохраняется.

## Ограничения

- Изображения: части `image_url` заменяются текстовой заглушкой — модель картинку не видит (`vision: false` в возможностях).
- Из браузера API доступен только с доменов JoinGonka (проверка `Origin`): вызывайте его со своего сервера, ключ во фронтенд не кладите.
- Эмбеддинги: `POST /v1/embeddings` отвечает `501` — в сети нет эмбеддинговых моделей.
- Коды ошибок, лимиты и таймауты — в разделе [Ошибки и лимиты](https://gate.joingonka.ai/ru/docs/errors).
