> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.beatapi.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.beatapi.io/_mcp/server.

# Decisions API

> Передавайте состояние приложения, необязательные изображения и типизированные вопросы. Получайте типизированные ответы с вероятностями, включая координаты объектов на изображении. Проверяйте ответ перед действием.

# Decisions API

Большинство моделей возвращают абзац текста, из которого приложению ещё нужно извлечь решение: запросить JSON, разобрать и проверить его, а при отклонении формата повторить запрос. Decisions API убирает этот этап. Вы передаёте текущее состояние приложения и вопросы. Каждый ответ уже имеет тип: вероятность «да», один из заданных вариантов или значение на вашей шкале, с указанием вероятности.

API предназначен для решений внутри программ, а не для диалога: распределения обращений, разрешения вызовов инструментов, приоритизации очередей и оценки заявок перед проверкой человеком.

`POST https://api.beatapi.io/v1/systemone`

Это путь эндпоинта TypeSafe с их же структурой запроса и ответа. В клиенте TypeSafe или его SDK достаточно изменить базовый URL и имя модели. `POST /v1/decisions` также доступен как псевдоним.

| Модель          | Когда использовать                                                                                                                                                    |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `jev-1.14-free` | **Начните здесь: бесплатный JEV.** Ввод и вывод стоят \$0, даже при нулевом балансе. Поддерживает изображения и вопросы `box`. См. раздел **Бесплатные вызовы** ниже. |
| `jev-1.14`      | Необязательная платная модель. $0.0252 за 1 млн входных токенов (на 40 % ниже базовой цены $0.042); вывод не оплачивается. Поддерживает изображения и вопросы `box`.  |
| `jev-1.13-free` | Предыдущая бесплатная версия, доступная на тех же условиях, что и `jev-1.14-free`. Поддерживает изображения, но не `box`.                                             |
| `jev-1.13`      | Предыдущая платная версия, доступная по той же цене, что и `jev-1.14`. Поддерживает изображения, но не `box`.                                                         |

`jev-1.14` соответствует `jev-1.14.0` от TypeSafe: умеренное улучшение относительно 1.13 по нескольким направлениям, особенно для многоязычного ввода. TypeSafe не опубликовала численные показатели, поэтому здесь они не приводятся. Цена осталась прежней.

> **Note**
>
> Это не `/v1/chat/completions`: модели JEV там недоступны. В запросе нет `messages`, а в ответе нет `choices`. Модель принятия решений не генерирует текст.

## Быстрый старт

Начните с `jev-1.14-free`: ввод и вывод стоят **\$0**, даже при нулевом балансе. Используйте ключ в стандартной группе **auto**. У бесплатных вызовов отдельный лимит: **один успешный запрос в минуту** до первого пополнения, до **десяти в минуту** после любого пополнения. См. [Бесплатные вызовы](#free-calls).

```bash
curl https://api.beatapi.io/v1/systemone \
  -H "Authorization: Bearer $BEATAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "jev-1.14-free",
    "state": "Task: clean up inactive accounts before the quarterly report.\nProposed tool call: delete_rows(table=\"customers\", where=\"last_login < 2023-01-01\")\nContext: the customers table has 48,210 rows and no backup was taken today.",
    "questions": {
      "safe_to_run": {
        "type": "noul",
        "instructions": "Is this action safe to run without a human approving it first?",
        "criteria": {
          "true": "Reversible or low-impact, and clearly within the stated task.",
          "false": "Destructive, irreversible, or broader than the task requires."
        }
      }
    }
  }'
```

Следующий ответ приведён для иллюстрации, это не результат измерения. Разберите JSON, проверьте HTTP-статус и тип ответа, затем проверьте значения перед применением своих порогов.

```json
{
  "id": "task_01J...",
  "model": "jev-1.14-free",
  "answers": {
    "safe_to_run": { "type": "noul", "noul": 0.04 }
  },
  "usage": { "input_tokens": 384, "output_tokens": 22 }
}
```

`0.04` означает оценённую моделью вероятность ответа *да*: четыре процента. Поэтому агент останавливается и обращается к человеку, ради чего и задавался вопрос.

### Типизированное решение без цикла исправления JSON

Укажите BeatAPI, отправьте состояние и вопросы, прочитайте ответы. Псевдоним `/v1/decisions` работает так же. Это не chat/completions: модели JEV там недоступны.

```javascript
const response = await fetch("https://api.beatapi.io/v1/systemone", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BEATAPI_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "jev-1.14-free",
    state: {
      task: "clean up inactive accounts",
      tool: 'delete_rows(table="customers", where="last_login < 2023-01-01")',
      rows: 48210,
      backup_today: false,
    },
    questions: {
      safe_to_run: {
        type: "noul",
        instructions: "Is this action safe to run without a human approving it first?",
        criteria: {
          true: "Reversible or low-impact, and clearly within the stated task.",
          false: "Destructive, irreversible, or broader than the task requires.",
        },
      },
    },
  }),
});

if (!response.ok) {
  // On HTTP 429, wait the Retry-After seconds before retrying.
  throw new Error(`JEV HTTP ${response.status}; Retry-After: ${response.headers.get("Retry-After")}`);
}
const decision = await response.json();
const answer = decision.answers?.safe_to_run;
if (answer?.type !== "noul" || !Number.isFinite(answer.noul)
    || answer.noul < 0 || answer.noul > 1) {
  throw new Error("Invalid JEV answer; route to human review");
}
// Illustrative threshold: evaluate it on your own cases before use.
const needsReview = answer.noul < 0.9;
console.log({ probability: answer.noul, needsReview });
```

## Запрос

| Поле        | Тип                       | Обязательно | Описание                                                                                                                                                                                                                                                          |
| ----------- | ------------------------- | ----------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`     | string                    |          Да | `jev-1.14` или `jev-1.14-free` для бесплатного вызова. `jev-1.13` и `jev-1.13-free` также принимаются. Запрос без модели отклоняется.                                                                                                                             |
| `state`     | string \| object \| array |          Да | Всё, что приложение знает сейчас: строка, JSON-объект или массив текстовых значений. Для изображения задайте его в `assets` и сошлитесь на него в тексте (см. [Изображения](#images)). Аудио, видео и другие двоичные данные сначала нужно преобразовать в текст. |
| `questions` | object                    |          Да | Один или несколько именованных вопросов. Выбранное вами имя станет ключом ответа.                                                                                                                                                                                 |
| `assets`    | object                    |         Нет | Изображения, упомянутые в тексте. Ключом служит выбранная строка ссылки, например `"<door>"`. Обязательно для вопросов `box`. См. [Изображения](#images).                                                                                                         |

Каждый вопрос содержит `type`, `instructions` и, в зависимости от типа, `criteria`.

| Поле           | Тип                       |        Обязательно | Описание                                                                                                                                                                                  |
| -------------- | ------------------------- | -----------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`         | string                    |                 Да | `noul`, `choice`, `score` или `box`.                                                                                                                                                      |
| `instructions` | string \| object \| array | Для `noul` и `box` | Вопрос на обычном языке, как для коллеги. Обязателен для `noul`, чтобы задать смысл «да/нет», и для `box`, чтобы указать искомый объект. Для `choice` и `score` может хватить `criteria`. |
| `criteria`     | см. ниже                  |    Зависит от типа | Значение вариантов ответа. Для `box` не принимается.                                                                                                                                      |

## Четыре типа вопросов

### `noul`: калиброванная вероятность

Для решения «да/нет» по заданному порогу, а не случайного выбора. Ответ представляет собой число от `0` до `1`.

```json
"refund_ok": {
  "type": "noul",
  "instructions": "Should this refund be approved automatically?",
  "criteria": {
    "true": "Clear billing error, under $100, first request from this account.",
    "false": "Disputed usage, over $100, or a repeat request."
  }
}
```

```json
"refund_ok": { "type": "noul", "noul": 0.83 }
```

`criteria` здесь необязателен, но описание обеих сторон уточняет ответ. Выберите свои пороги: например, автоматически одобрять выше `0.9`, передавать на следующий уровень ниже `0.6`, а промежуточные случаи ставить в очередь на проверку.

> **Note**
>
> `noul` — настоящее имя типа, не опечатка вместо `bool`. Неверное имя приводит к ошибке 400 с перечнем допустимых типов.

### `choice`: один из заданных вариантов

`criteria` — объект, сопоставляющий имя каждого варианта с его смыслом. Ответ содержит выбранный вариант и полное распределение вероятностей.

```json
"route": {
  "type": "choice",
  "instructions": "Which queue should this ticket go to?",
  "criteria": {
    "billing": "Payment, invoices, refunds, or charges.",
    "technical": "The product is broken or erroring.",
    "sales": "Pre-purchase questions about plans or pricing."
  }
}
```

```json
"route": {
  "type": "choice",
  "choice": "billing",
  "probabilities": { "billing": 1, "technical": 0, "sales": 0 },
  "confidence": 1
}
```

### `score`: положение на вашей шкале

`criteria` — массив описаний шкалы от меньшего значения к большему. Ответ — непрерывное значение по **индексам массива**, начиная с `0`.

```json
"urgency": {
  "type": "score",
  "instructions": "How urgent is this ticket?",
  "criteria": [
    "1 - can wait a week",
    "2 - answer within a few days",
    "3 - answer today",
    "4 - answer within an hour",
    "5 - wake someone up"
  ]
}
```

```json
"urgency": {
  "type": "score",
  "score": 1.98,
  "legend": { "0": "1 - can wait a week", "1": "2 - answer within a few days",
              "2": "3 - answer today", "3": "4 - answer within an hour",
              "4": "5 - wake someone up" },
  "probabilities": { "0": 0, "1": 0.17, "2": 0.67, "3": 0.16, "4": 0 },
  "confidence": 0.72
}
```

`1.98` находится между `legend["1"]` и `legend["2"]`, почти у второго значения: «ответить сегодня». Округляйте для выбора категории, сохраняйте дробную часть для сортировки очереди.

### `box`: положение объекта на изображении

Используйте для поиска одного объекта. `box` поддерживают только `jev-1.14` и `jev-1.14-free`. В `instructions` укажите, что искать; `criteria` не допускается. В запросе должно быть хотя бы одно изображение (см. [Изображения](#images)), иначе вернётся ошибка 400. Модели `jev-1.13` и `jev-1.13-free` отклоняют вопросы `box`.

```json
"where": {
  "type": "box",
  "instructions": "The red rectangle in <picture>."
}
```

```json
"where": {
  "type": "box",
  "box": { "top": 0.12, "bottom": 0.43, "left": 0.56, "right": 0.89 },
  "confidence": 0.93
}
```

Четыре значения — доли от `0` до `1` от высоты (`top`, `bottom`) и ширины (`left`, `right`), отсчитываемые от верхнего и левого края. Умножьте их на размеры изображения в пикселях, чтобы получить координаты. `confidence` может быть `null`; проверяйте это перед сравнением с порогом.

## Изображения

Модели 1.13 и 1.14 обрабатывают изображения. Задайте каждое один раз в объекте `assets` верхнего уровня, а строку ссылки поместите в текст `state`, в `instructions` вопроса или в значение `criteria`. Изображение вставляется в месте ссылки.

| Поле          | Тип    | Обязательно | Описание                                                     |
| ------------- | ------ | ----------: | ------------------------------------------------------------ |
| `type`        | string |         Нет | `image`, можно опустить.                                     |
| `format`      | string |          Да | `png`, `jpg` или `jpeg`.                                     |
| `data_base64` | string |          Да | Байты изображения в стандартном base64 без префикса `data:`. |

```bash
curl https://api.beatapi.io/v1/systemone \
  -H "Authorization: Bearer $BEATAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "jev-1.14-free",
    "state": "Check the uploaded image.",
    "assets": {
      "<picture>": { "type": "image", "format": "png", "data_base64": "iVBORw0KGgo..." }
    },
    "questions": {
      "red":   { "type": "noul", "instructions": "Is there a red rectangle in <picture>?" },
      "where": { "type": "box",  "instructions": "The red rectangle in <picture>." }
    }
  }'
```

В этом вызове использовалось белое изображение 280×280 пикселей с красным прямоугольником. Ниже приведён измеренный результат. `id` — идентификатор запроса BeatAPI, добавленный к полям поставщика.

```json
{
  "id": "task_01J...",
  "model": "jev-1.14-free",
  "answers": {
    "red":   { "type": "noul", "noul": 0.92 },
    "where": { "type": "box",
               "box": { "top": 0.12, "bottom": 0.43, "left": 0.56, "right": 0.89 },
               "confidence": 0.93 }
  },
  "usage": { "input_tokens": 501, "output_tokens": 36 }
}
```

Правила ссылок:

* Ссылка сопоставляется как обычный текст. Подойдёт любая строка, например `"<door>"` или `"[img-1]"`. Ключи объектов не просматриваются; размещайте ссылку в значении.
* Каждая заданная ссылка должна использоваться хотя бы раз, иначе запрос отклоняется с ошибкой 400.
* Отправляйте только JSON. Multipart не поддерживается. Поле `part` в ресурсе вызывает ошибку 400 с просьбой использовать `data_base64`.
* Заголовок `TypeSafe-Danger-Zone-Images`, обязательный в API TypeSafe, отправлять не нужно: BeatAPI добавит его сам. Передать его всё же можно.

Ограничения изображений:

* Только статические PNG или JPEG. Анимированные изображения отклоняются.
* До 16 MiB на изображение после декодирования и до 32 MiB на всё тело запроса.
* Каждая сторона — от 280 до 1568 пикселей. При размере ниже минимума вернётся `Invalid image dimensions.`
* Автоматического изменения размера нет. Уменьшите слишком большое изображение перед отправкой.
* Одно некорректное изображение приводит к отказу всего запроса.

## Несколько вопросов в одном вызове

Задавайте все вопросы об одном состоянии одновременно. Ответы независимы, а общее состояние учитывается один раз, не для каждого вопроса. Вызовы `jev-1.14-free` остаются бесплатными.

```bash
curl https://api.beatapi.io/v1/systemone \
  -H "Authorization: Bearer $BEATAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "jev-1.14-free",
    "state": {"ticket": "charged twice this month", "plan": "pro", "tenure_days": 412},
    "questions": {
      "refund_ok": {"type": "noul", "instructions": "Approve the refund automatically?"},
      "urgency":   {"type": "score", "instructions": "How urgent?", "criteria": ["low", "medium", "high"]}
    }
  }'
```

## Ответ

| Поле      | Описание                                                                                                                                                                |
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`   | Модель, которая ответила.                                                                                                                                               |
| `answers` | По одной записи на вопрос под заданным вами именем. В записи есть `type` и поля из таблицы ниже.                                                                        |
| `usage`   | `input_tokens` и `output_tokens` этого вызова.                                                                                                                          |
| `id`      | Идентификатор запроса BeatAPI; указывайте его при обращении в поддержку. Добавляется к трём полям поставщика. Клиенты, не описывающие это поле, могут его игнорировать. |

Поля ответа по типу вопроса:

| `type`   | Поля                                                                                                                                                    |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `noul`   | `noul`: вероятность *да*, от `0` до `1`.                                                                                                                |
| `choice` | `choice`: выбранный вариант; `probabilities`: значение для каждого варианта; `confidence`.                                                              |
| `score`  | `score`: значение по индексам `criteria`; `legend`; `probabilities`; `confidence`.                                                                      |
| `box`    | `box`: `top`, `bottom`, `left`, `right`, доли от `0` до `1` высоты или ширины, отсчитываемые от верхнего и левого края; `confidence` может быть `null`. |

Ответ синхронный: успешный HTTP-ответ означает, что решение готово. Не нужно опрашивать статус или скачивать файлы результата.

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

* **Контекст:** 64K токенов суммарно для `state` и всех вопросов. `state` вместе с самым длинным вопросом должны помещаться в 32K токенов. См. [ограничения моделей](https://docs.typesafe.ai/models).
* **Расширенный контекст пока не поддерживается.** У TypeSafe есть заголовок для увеличения контекста до 262K токенов, но BeatAPI его не поддерживает. Действует лимит 64K, включая токены изображений.
* **Изображения:** только JSON, без multipart, с ограничениями раздела [Изображения](#images).
* **Нет параметров сэмплирования.** `temperature`, `top_p`, `seed` и аналогичные параметры не принимаются. Формат ответа детерминирован по замыслу.
* **Нет потоковой выдачи.** Ответ — значение, а не последовательность токенов.
* **Для `choice` нужен непустой объект `criteria`, для `score` — непустой массив `criteria`.** Иначе вернётся ошибка 400 с пояснением. `instructions` необязателен, но пустое значение отклоняется.

## Ошибки

Ошибки используют стандартный формат BeatAPI и всегда содержат `retryable`. Полная таблица и правила повторных запросов приведены в разделе [Ошибки](/errors).

| HTTP | Код                                                    | Значение                                                                                                                                                                                                                              |
| ---: | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  400 | `bad_request`                                          | Некорректный JSON, модель, тип, критерии или изображение; сообщение указывает поле. Например: неиспользуемая или неверная запись `assets`, поле `part`, `box` без изображения, неподдерживаемое изображение или размер вне диапазона. |
|  401 | `missing_api_key` / `invalid_api_key`                  | Ключ отсутствует, неверен, просрочен, отключён или его квота исчерпана.                                                                                                                                                               |
|  402 | `insufficient_credits`                                 | Нулевой баланс или недостаточно средств для вызова.                                                                                                                                                                                   |
|  403 | `forbidden`                                            | Ключ или аккаунт не имеет доступа к модели, например если она недоступна за подарочные кредиты.                                                                                                                                       |
|  404 | `not_found`                                            | Неизвестная модель.                                                                                                                                                                                                                   |
|  429 | `rate_limit_exceeded`                                  | Достигнут лимит запросов, включая бесплатную модель. Подождите число секунд из `Retry-After`.                                                                                                                                         |
|  500 | `internal_error`                                       | Неожиданная внутренняя ошибка; запрос можно повторить.                                                                                                                                                                                |
|  503 | `processing_unavailable` или другой код `processing_*` | Не удалось завершить запрос. Повторяйте с увеличением интервала.                                                                                                                                                                      |

Неуспешные вызовы не расходуют кредиты.

## Оплата

Платные `jev-1.14` и `jev-1.13` тарифицируются **только по входным токенам** в `state`, `questions` и изображениях: \*\*$0.0252 за 1 млн входных токенов**, на 40 % ниже базовой цены $0.042. Вывод не оплачивается: это типизированное значение, а не сгенерированный текст. Точный расход каждого вызова указан в `usage.input_tokens`, что позволяет сверить затраты.

Изображения оплачиваются по той же ставке за входные токены, без отдельной платы за изображение. Ориентиры: 280×280 — около 320 токенов, 512×512 — около 840, 1024×768 — около 2 200. Фактическое число указано в `usage.input_tokens`.

Поэтому несколько вопросов об одном состоянии в одном вызове заметно дешевле, чем повторная отправка того же состояния.

### Бесплатные вызовы

При `"model": "jev-1.14-free"` вызов ничего не стоит. Для `jev-1.13-free` действуют те же условия. Это те же модели, что и платные аналоги, с теми же структурами запроса и ответа. В ответе сохраняется запрошенное имя модели. Они работают при нулевом балансе, поэтому интеграцию можно начать до пополнения.

* Доступ через стандартную группу **auto**, назначаемую каждому новому ключу. Ключ, закреплённый за отдельной группой, не имеет доступа.
* У бесплатных вызовов фиксированный отдельный лимит на аккаунт: **один успешный запрос в минуту** до первого пополнения, затем не более **десяти в минуту** после пополнения на любую сумму. Счётчик независим от обычного лимита аккаунта и не расходует квоту платных вызовов.
* При превышении вернётся `429 rate_limit_exceeded` с `Retry-After`. Подождите указанное время.
* Платный `jev-1.14` доступен по ставке выше и использует обычный лимит аккаунта, растущий с общей суммой пополнений. Для его выбора измените только имя модели.

### Более высокие лимиты запросов

После любого пополнения `jev-1.14-free` остаётся бесплатным и допускает не более **10 успешных вызовов в минуту**. Платные вызовы `jev-1.14` используют общую квоту аккаунта. Текущий лимит и следующий порог пополнения указаны в панели управления; отдельной квоты JEV нет.

[Пополните баланс](https://beatapi.io/dashboard/billing), затем явно измените `model` с `jev-1.14-free` на `jev-1.14`, если нужны платные вызовы. Эндпоинт, `state` и `questions` остаются прежними. Ввод стоит \*\*$0.0252 за 1 млн токенов** (базовая цена $0.042, скидка 40 %), вывод бесплатный. Пополнение не переключает модель автоматически. Для платных вызовов по-прежнему нужен достаточный баланс. При 429 соблюдайте `Retry-After`.