# Запуск генерации

> Полный путь через REST API Строфи: каталог моделей, загрузка входных файлов, оценка стоимости, запуск с защитой от повтора, ожидание исхода, результат и отмена.

Источник: https://strophe.app/docs/product/developers/generation
Обновлено: 2026-08-26

Генерация запускается одним запросом и выполняется в очереди: ответ на запуск несёт идентификатор, по которому забирают результат. Ниже — весь путь, от выбора модели до готового файла.

## Каталог моделей

Список семейств отдаёт `GET /api/v1/families` под правом `catalog:read`. Фильтры — `task` (что делает модель) и `outputType` (что получается на выходе):

```bash
curl "https://strophe.app/api/v1/families?outputType=image" \
  -H "Authorization: Bearer <ваш ключ>"
```

Подробности одного семейства — `GET /api/v1/families/{id}`. В ответе варианты модели с их задачами и медиа-входами, настраиваемые параметры с типами и допустимыми значениями, цены и ожидаемое время работы.

Там же оси выбора варианта — `axes`: чем семейство отличает свои варианты помимо формы входа. Чаще всего это уровень качества (`version`), реже размер (`size`), тир (`tier`), режим (`mode`) или формат вывода (`format`). У каждой оси перечислены допустимые значения и значение по умолчанию, а у каждого варианта — его значения осей: по ним видно, во что обходится каждый уровень.

Недоступные вам семейства из выдачи не исчезают: они приходят с `available: false` и причиной в `lockReason`. Пропустить их молча значило бы соврать, что такой модели нет.

Неизвестное имя параметра запроса — отказ 400, а не полная выдача. Опечатка вроде `?tasks=` иначе выглядела бы как применённый фильтр.

Общее устройство каталога описано в статье [Каталог моделей](https://strophe.app/docs/product/generation/model-catalog).

## Входные файлы

Медиа задаётся только нашими идентификаторами. Внешняя ссылка в генерацию не попадает: путь чужого файла внутрь один — `POST /api/v1/files` под правом `files:write`.

Файл принимается тремя способами: телом `multipart/form-data` (поле `file`), ссылкой или строкой base64.

```bash
curl https://strophe.app/api/v1/files \
  -H "Authorization: Bearer <ваш ключ>" \
  -F file=@reference.png
```

```bash
curl https://strophe.app/api/v1/files \
  -H "Authorization: Bearer <ваш ключ>" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/reference.png"}'
```

Предел файла — 25 МБ, одинаково для всех трёх способов.

Тип решает подпись содержимого, а не заявление вызывающего: `mimeType` в запросе можно передать, но если он противоречит содержимому, файл будет отвергнут.

В ответе — `fileId`, который и подставляется во входы генерации. Состояние `processing` не отказ: идентификатор уже действителен, а тяжёлое видео дообработается само. Запуск с ещё не готовым файлом отвечает `file_not_ready` — тот же запрос через несколько секунд пройдёт.

Результат прошлой генерации годится входом наравне с загруженным файлом: у готовой генерации есть поле `reusableAs` со списком полей запроса, куда её можно подать, а значением служит `generationId`.

## Оценка стоимости

Сколько будет стоить запрос, отвечает `POST /api/v1/generations/estimate` под правом `generations:read`. Тело — то же, что у запуска. Ничего не создаётся и не тратится:

```bash
curl https://strophe.app/api/v1/generations/estimate \
  -H "Authorization: Bearer <ваш ключ>" \
  -H "Content-Type: application/json" \
  -d '{"familyId": "nano-banana-pro", "prompt": "a red bicycle on a wet street"}'
```

В ответе — стоимость в кредитах, вариант модели, который будет запущен, доступные кредиты и признак `sufficient`: хватает ли счёта на эту работу прямо сейчас. Оценка считается так же, как окончательное списание, поэтому расхождения между ними нет.

## Запуск

`POST /api/v1/generations` под правом `generations:write`:

```bash
curl https://strophe.app/api/v1/generations \
  -H "Authorization: Bearer <ваш ключ>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4821-render-1" \
  -d '{
        "familyId": "nano-banana-pro",
        "prompt": "a red bicycle on a wet street, evening light",
        "imageIds": ["3f2a…"],
        "maxCredits": 40
      }'
```

Обязателен только `familyId`. Подо что берётся вход, сервер читает по форме запроса. Уровень качества он угадать не может: его задают объектом `axes`, например `{"version": "pro"}`; пропущенная ось берёт значение по умолчанию. Параметры семейства идут объектом `parameters`, медиа — списками идентификаторов (`imageIds`, `videoId`, `videoIds`, `model3dIds`, `audioIds`), и порядок в списках значим: входы моделей позиционные.

`modelId` называет вариант целиком — и сценарий, и уровень; его передают, когда вариант нужен конкретный. Вместе с `axes` он не задаётся: два разных выбора в одном запросе — отказ, а не приоритет одного над другим. Имя оси или значение, которых у семейства нет, тоже отказ; значение, которого нет ни у одного варианта под эту форму входа, — отдельный отказ со списком доступных здесь.

`maxCredits` — потолок этого запроса: дороже — отказ до траты денег. Он необязателен, потому что точную цену даёт оценка, но в автоматической работе его стоит ставить всегда.

Тело проверяется строго: неизвестное поле — отказ, а не молчаливое отбрасывание. Опечатка в имени параметра иначе запустила бы не то, что вы просили, а счёт пришёл бы настоящий. То же внутри `parameters`: имя, которого нет в схеме семейства, — отказ, и медиа туда не кладут, даже если параметр модели называется ссылкой. Входы задаются только списками идентификаторов.

Ответ — 201 с идентификатором, состоянием `queued`, удержанной суммой и заголовком `Location` на ручку состояния.

## Защита от повтора

Заголовок `Idempotency-Key` делает запуск безопасным при обрыве связи. Повтор с тем же ключом и тем же телом не запускает вторую генерацию: приходит тот же ответ 201, но с `replayed: true`.

Тот же ключ с другими параметрами — отказ `idempotency_conflict`. Повтор, пока первый запрос ещё выполняется, — `idempotency_in_progress`: дождитесь его, а не запускайте новый.

Без заголовка повтор ничем не защищён. Оборвавшийся ответ в этом случае означает, что генерация могла запуститься, — и деньги за неё будут списаны.

## Ожидание исхода

Состояние генерации отдаёт `GET /api/v1/generations/{id}` под правом `generations:read`. Параметр `wait` до 60 секунд задерживает ответ до конца работы или до истечения этого времени:

```bash
curl "https://strophe.app/api/v1/generations/<id>?wait=30" \
  -H "Authorization: Bearer <ваш ключ>"
```

Состояний шесть: `queued`, `running`, `finalizing` и три конечных — `ready`, `failed`, `cancelled`. У незавершённой генерации есть `pollAfterSeconds` — через сколько имеет смысл спросить снова; у конечных его нет, потому что спрашивать больше не о чем.

Опрос — не единственный способ: исход умеет приходить сам. Как это настроить, описано в статье [Вебхуки](https://strophe.app/docs/product/developers/webhooks).

Провалившаяся генерация — это ответ 200, а не отказ запроса: состояние `failed`, а причина лежит в `error` — класс `code`, причина внутри класса `reason`, признак `retryable`, подсказка `hint` и разобранные числа в `details`. Как устроен этот разбор — в статье [Ошибки и лимиты](https://strophe.app/docs/product/developers/errors-and-limits).

## Результат

У готовой генерации в `result` лежит ссылка на файл, его тип и размеры, а у видео и аудио — длительность. Ссылка ведёт на нашу выдачу, а не в хранилище: она ничего не раскрывает и живёт не меньше суток, но умирает вместе с ключом, которым выдана. Если ключ отозвали, выданные им ссылки перестают работать.

Поле `text` в результате занято текстовой генерацией. Каталог внешнего API таких семейств не предлагает — текст через него не запускается, — но в истории они встречаются.

## История

`GET /api/v1/generations` возвращает историю страницами: до 50 записей за раз, метка следующей страницы — в `nextCursor`. Фильтр `status` принимает `active`, `ready` и `failed`.

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

История общая с подключением агента по MCP: это один внешний доступ с двумя способами обращения, и делить их незачем.

## Отмена

`POST /api/v1/generations/{id}/cancel` под правом `generations:write`. В ответе — состояние, признак `cancelled` (отменил ли генерацию именно этот вызов) и `creditsCharged`.

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