# Запросы

Запрос — это один вызов одной модели. У него есть идентификатор (`req_…`), статус, стоимость в API-кредитах (1000 = 1 USD) и, если он выполнен успешно, сгенерированные файлы. Один и тот же объект возвращается каждым эндпоинтом ниже для любой модели.

## Найти модель

Каталог всех 1 496 моделей доступен в открытом JSON (ключ не нужен):

```bash
curl "https://api.doitong.ru/v1/models?category=text-to-video&lab=Google&limit=20"
```

| Параметр запроса | Значение |
|---|---|
| `q` | Все слова должны встречаться в названии, идентификаторе, названии разработчика или тегах, например `kling 3`. |
| `category` | Один идентификатор категории, например `text-to-video` или `upscale`. `GET https://api.doitong.ru/v1/categories` перечисляет все 21 категории с их количеством. |
| `lab` | Разработчик модели — по названию или идентификатору, например `Google` или `black-forest-labs`. |
| `family` | Идентификатор `family` из предыдущей строки: все варианты одной модели (pro, fast, image-to-video, …). |
| `mode` | `exact` (цена рассчитывается по входным данным), `measured` (по длительности или размеру входных файлов), `estimate` (расчётная цена за запуск) или `metered` (оплата по фактическому использованию, до максимальной суммы за запуск). |
| `limit`, `cursor` | Размер страницы (от 1 до 100, по умолчанию 50) и `next_cursor` предыдущей страницы. |

Каждая строка содержит `id`, `name`, `lab`, `category`, `family`, `summary`, `tags`, `output_kind`, `docs_url` и объект `price` (см. [Цены](https://doitong.com/developers/docs/pricing#price-objects)). `GET https://api.doitong.ru/v1/models/{model_id}` дополнительно возвращает полную JSON Schema входных и выходных данных, канонические имена параметров, правила расчёта цены и пример входных данных. Неизвестные значения `category` или `mode` возвращают `400 invalid_parameter`.

## Запустить модель

```bash
curl -X POST "https://api.doitong.ru/v1/run/{model_id}?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1842-cover" \
  -d '{"prompt": "..."}'
```

- Тело запроса — входные данные модели, объект JSON. Параметры перечислены на странице каждой модели.
- Входные данные проверяются до списания средств. Некорректные данные возвращают `422 invalid_input` с ошибочным `param` и ничего не стоят.
- `?wait=N` (до 20 секунд) удерживает вызов, пока запрос не завершится или не истечёт время.
- `?webhook_url=https://…` отправляет подписанный POST на этот URL после завершения запроса (см. [Вебхуки](https://doitong.com/developers/docs/webhooks)).
- `?storage=permanent` или `?storage=temporary` определяет, как долго хранятся выходные файлы; без этого параметра применяется настройка аккаунта (см. [Где хранятся файлы](#storage)).

Пока запрос выполняется, ответ имеет статус `202 Accepted`, а при завершении в течение времени ожидания — `200 OK` (или если это повтор, см. ниже). Заголовки `Location` и `Doitong-Request-Id` содержат URL и идентификатор запроса.

## Статусы

Жизненный цикл запроса: `queued` → `running` → `succeeded` | `failed` | `canceled` (финальный; неудачные и отменённые запросы возмещаются).

| Статус | Значение | Финальный |
|---|---|---|
| `queued` | Принят и оплачен, ожидает обработчика. `queue_position` может показывать место в очереди. | нет |
| `running` | Модель генерирует результат. | нет |
| `succeeded` | Выполнен. `outputs` содержит список файлов. | да |
| `failed` | Модель или ограничение остановили выполнение. `error` указывает причину. API-кредиты возвращаются. | да |
| `canceled` | Отменён до создания результата. API-кредиты возвращаются. | да |

Запрос, выполняющийся дольше установленного времени, завершается с кодом `timeout`, а средства возвращаются: 15 минут для изображений, 60 минут для видео, 30 минут для аудио и всего остального.

## Объект запроса

```json
{
  "id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
  "object": "request",
  "model": "flux/schnell",
  "status": "succeeded",
  "created_at": "2026-10-20T12:00:00Z",
  "started_at": "2026-10-20T12:00:01Z",
  "completed_at": "2026-10-20T12:00:02Z",
  "queue_position": null,
  "cost": { "credits": 3, "usd": "0.003", "charged": 3, "refunded": 0 },
  "input": { "prompt": "..." },
  "output": { "images": [ { "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "width": 1024, "height": 768 } ] },
  "outputs": [
    { "type": "image", "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "content_type": "image/jpeg",
      "width": 1024, "height": 768, "file_size": 183920, "stored": true }
  ],
  "error": null,
  "metrics": { "inference_seconds": 1.2 },
  "storage": { "mode": "permanent", "bytes": 183920, "expires_at": null, "expired": false },
  "urls": {
    "self": "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t",
    "cancel": "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel"
  }
}
```

- `outputs` одинаков для всех моделей: по одной записи на каждый сгенерированный файл с `type` (`image`, `video`, `audio`, `file` или `3d`), `url` в нашем хранилище, `content_type` и, если известны, `file_size`, `width`, `height`, `duration`.
- `output` — собственный объект результата модели (его поля перечислены на странице каждой модели) с теми же URL.
- `storage` показывает, как хранятся файлы: `mode` (`permanent` или `temporary`), сохранённые `bytes`, `expires_at` (время удаления временного файла, `null` для постоянных) и `expired` (`true`, когда файлы удалены).
- `cost.credits` — цена вызова в API-кредитах (`cost.usd` в USD), `cost.charged` — сумма, списанная с баланса API, `cost.refunded` — возвращённая сумма. Для модели `metered` `cost.credits` — максимальная сумма, удержанная в начале (`cost.held`); после успешного выполнения `cost.settled` показывает фактическую стоимость использования, а остаток максимальной суммы находится в `cost.refunded` (см. [Оплата по использованию](https://doitong.com/developers/docs/pricing#metered)).
- При ошибке запроса `error` имеет вид `{ "type": …, "code": …, "message": … }`.

## Получить запрос

```bash
curl "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- `?wait=N` (до 20 секунд) выполняет длительный опрос: вызов возвращается сразу после перехода запроса в финальный статус или через N секунд с текущим состоянием. Циклический опрос — самый простой способ дождаться результата.
- `?logs=1` добавляет строки журнала выполнения модели (последние 50) в поле `logs`.

Можно получать только запросы своего аккаунта; любой другой идентификатор возвращает `404 request_not_found`.

## Список запросов

```bash
curl "https://api.doitong.ru/v1/requests?limit=20&status=succeeded&model=flux/schnell" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

Сначала новые. `limit` — от 1 до 100 (по умолчанию 20), `status` и `model` выполняют фильтрацию. Ответ имеет вид `{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`; для следующей страницы передайте `cursor=<next_cursor>`.

## Отменить запрос

```bash
curl -X POST "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- Отменён до запуска модели: статус становится `canceled`, API-кредиты возвращаются, ответ — `200`.
- Генерация уже началась: запрос на отмену передаётся модели, ответ — `202` с `"cancel_requested": true`. Если модель всё же завершит работу, запрос будет успешно выполнен и оплачен.
- Уже финальный статус: `409 not_cancelable` (уже отменённый запрос просто возвращает `200`).

## Удалить файлы запроса

```bash
curl -X DELETE "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/files" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

Немедленно удаляет наши копии выходных файлов запроса и прекращает списание за их хранение. Ответом служит объект запроса с `storage.expired: true`; URL остаются в записи, но больше не работают. Повторный вызов ничего не меняет (`200`). У запроса со статусом `queued` или `running` ещё нет файлов: `409 request_active`. Требуется `models:run`.

## Идемпотентность: повтор без двойной оплаты

Сети могут давать сбои. Чтобы безопасно повторить запуск, отправьте заголовок `Idempotency-Key` (любая строка длиной до 255 символов, уникальная для операции, например собственный идентификатор заказа):

- Тот же ключ и то же тело: вы получите исходный запрос (`200`, заголовок `Idempotent-Replayed: true`), повторного списания не будет.
- Тот же ключ и тело после запуска, завершившегося до передачи модели (`upstream_error` или `internal_error` при отправке, `insufficient_credits`, `budget_exceeded`, `too_many_inflight`): результат не создан и средства не списаны, поэтому повторный запрос снова запустит модель, а не вернёт ошибку.
- Тот же ключ с другим телом: `409 idempotency_mismatch`.
- Ключи привязаны к отправившему их API-ключу.

## Где хранятся файлы

Каждый выходной файл копируется в наше хранилище до того, как запрос получает статус `succeeded`, поэтому URL в `outputs` ведут на наши хосты и их можно использовать напрямую. Вы сами выбираете срок хранения:

| Режим | Срок | Цена |
|---|---|---|
| `permanent` (по умолчанию) | Пока вы не удалите файлы | 100 API-кредитов (0,10 $) за ГБ в месяц, списание ежедневно с баланса API |
| `temporary` | 7 дней, затем удаление | Бесплатно |

- Настройка аккаунта определяет режим для новых запросов: переключатель «Хранить файлы навсегда» в [панели управления](https://doitong.com/developers/dashboard#balance) или `PATCH https://api.doitong.ru/v1/account/storage` с `{"mode": "temporary"}`. `GET https://api.doitong.ru/v1/account` показывает её в `storage`, вместе с объёмом хранимых данных и месячной оценкой стоимости.
- `?storage=permanent|temporary` при запуске (аргумент `storage` инструмента MCP `run`) переопределяет её для этого запуска.
- Изменение настройки не затрагивает уже сохранённые файлы; удалите их с помощью [`DELETE /requests/{id}/files`](#delete-files).
- Если на балансе API недостаточно средств для хранения, списание не производится и вы получаете письмо: пополните баланс в течение 30 дней, иначе сохранённые файлы станут временными и будут удалены через 7 дней.

В редком случае сбоя при копировании запись содержит `"stored": false`, а URL является исходным URL с коротким сроком действия: скачайте файл как можно скорее.
