Видео и API · обновлено 20 октября 2026 г. · 10 минут
Единый API для генерации видео: как устроить приложение без потерянных заказов
Пользователь нажал «Создать видео», закрыл страницу и вернулся через несколько минут. Хорошая интеграция покажет прежний заказ, его результат и расход. Разберём архитектуру единого API видеогенерации на примере сервиса, который делает короткие ролики для карточек товаров.

Начинайте с заказа пользователя, а не с HTTP-запроса
Представьте магазин: продавец загрузил фотографию кроссовка и попросил сделать плавный облёт. Для него это один заказ, даже если браузер повторил отправку, соединение оборвалось или обработчик перезапустился. Поэтому сначала создайте собственный номер заказа. Затем привяжите к нему точную модель, операцию, материалы, параметры и будущий ID задания Gate.
Разделите собственный заказ и работу провайдера. Ваш статус «подготовка» может означать проверку исходников, а статус Gate — выполнение генерации. Такое разделение позволяет честно объяснять ожидание: файл ещё загружается, запрос принят, модель работает или подтверждается расход. Пользователь видит последовательный процесс вместо бесконечно вращающейся кнопки.
В этой статье показана схема вашего приложения. Названия таблиц и внутренних статусов ниже не являются дополнительными endpoint Gate. Для простого первого запроса есть отдельный quickstart; здесь внимание уделено тому, что произойдёт со вторым нажатием, закрытой вкладкой и неожиданным перезапуском.
- Заказ пользователя: Одно намерение и собственный номер
- Проверка и фиксация: Схема, тело и ключ
- Задание Gate: ID и восстановимое ожидание
- Файл и учёт: Результат и окончательное списание
Разделите браузер, backend и обработчик очереди
Браузер собирает описание и материалы, показывает настройки и статус. Ваш backend проверяет авторизацию пользователя, доступный бюджет и принадлежность материалов, затем обращается к Gate. Ключ Celestria Gate хранится только на этой серверной стороне. В публичный JavaScript и скопированный клиентом JSON секрет не попадает.
Обработчик очереди занимается длительным ожиданием отдельно от открытой страницы. Он получает существующее задание по ID, сохраняет следующий статус и планирует очередную проверку. Не обязательно держать один HTTP-запрос браузера открытым несколько минут: вкладка может получать короткие ответы о состоянии вашего заказа. Это архитектурное решение приложения, а не обещание готового webhook Gate.
Для первого прототипа backend и обработчик могут быть одним процессом. Важно, чтобы состояние было в постоянном хранилище, а не только в памяти. Позднее их можно разделить без смены публичной формы заказа. Количество одновременно выполняемых задач ограничивайте своими средствами и реальными лимитами аккаунта.
| Часть приложения | Что хранит и делает |
|---|---|
| Браузер | Описание, разрешённые исходники и понятный пользователю статус |
| Backend | Авторизация, ключ Gate, проверка и создание заказа |
| Постоянное хранилище | Точное тело, Idempotency-Key, ID Gate и итоговый billing |
| Обработчик | Опрос существующего задания, сохранение прогресса и результата |
Зафиксируйте содержание до платного запуска
Сначала определите тип задачи: новая сцена, оживление фотографии или редактирование видео. Найдите точный канонический ID и операцию в каталоге, затем загрузите её схему через GET /v1/media/models/schema?model=…&operation=…. Для всех выбранных моделей действует один контракт. Входные поля различаются по возможностям модели, а совместимый провайдер определяется Gate до платной отправки.
Покажите оценку, если она доступна для общего маршрута через /v1/media/estimate. При согласованной отправке сохраните неизменяемое тело заказа. Изменение длительности, звука или исходной фотографии после этого создаёт новый вариант, а не незаметно переписывает уже работающий. В истории полезно показывать, какой именно вариант был выполнен.
Ниже — учебный объект вашего приложения, не тело вызова Gate. request должен содержать настоящий model и input, собранные по схеме. client_order_id и состояние интерфейса остаются вашей ответственностью; не добавляйте их произвольно в API, который принимает строгий контракт.
{
"client_order_id": "shop-clip-001",
"state": "prepared",
"request": {
"model": "kwaivgi/kling-v3.0-pro",
"input": {
"operation": "text-to-video",
"prompt": "One sneaker on a studio pedestal, slowly rotating. A fixed camera.",
"duration": 5,
"aspect_ratio": "16:9",
"generate_audio": false
}
},
"idempotency_key": "shop-clip-001-v1",
"gate_job_id": null
}
Сделайте повторную отправку продолжением прежнего заказа
Сохраните Idempotency-Key до сетевого POST. Для нового самостоятельного заказа используйте новое значение; для восстановления прежнего оставляйте прежнее тело и ключ. В пределах организации повтор возвращает то же задание и сохранённый маршрут. Если тело изменено, тот же ключ приводит к конфликту 409. Это полезная защита от смешивания разных заказов.
Не считайте сетевой тайм-аут доказательством, что сервер ничего не получил. Backend мог передать работу дальше, а ответ потерялся. Такой заказ должен перейти в состояние выяснения, а не автоматически получить новый ключ. Если ID уже сохранён, используйте GET. Если ID неизвестен, восстановите исходный POST с прежним ключом.
При двух быстрых нажатиях проверяйте свой номер заказа атомарно в базе. Оба обработчика должны увидеть одну запись и один ключ. Защита Gate помогает на следующем уровне, но сама по себе не определяет, какие два действия вашего пользователя считаются одним намерением. Правило «новый дубль или новая версия» нужно задать в приложении.
Отображайте состояние без выдуманного процента готовности
Создание общего задания возвращает HTTP 202. Это означает принятую асинхронную работу, а не готовый файл. При queued, submitting, pending и in_progress продолжайте опрос с разумной паузой. При awaiting_billing подтверждается итоговый расход; не выдавайте предварительную оценку за финальную сумму. При completed можно показывать результат.
Не переводите время ожидания в художественный процент вроде «готово на 87%», если API не сообщает такой прогресс. Показывайте понятный этап и возможность вернуться позже. Временный сбой GET означает, что статус не удалось обновить: он не превращает существующую генерацию в новую и не доказывает её неудачу.
Для failed и cancelled сохраните ошибку и billing, остановите обычный цикл ожидания и покажите пользователю доступное действие. Новая попытка с изменённым заданием — отдельный заказ и возможное списание. Локальный тайм-аут обработчика не отменяет работу провайдера; после перезапуска продолжайте по прежнему ID.
Свяжите результат и расходы одной записью
Храните предварительную оценку отдельно от фактического billing.cost_rub. Поле reserved_rub описывает текущий резерв, который влияет на доступность денег во время работы. Это не дополнительная покупка поверх окончательного списания. Расход у провайдера в billing.upstream_cost_usd имеет другую валюту и назначение, чем рублёвая сумма клиента.
Для отчёта по заказу нужны как минимум модель, операция, выбранный provider, ID, окончательная сумма и время последнего обновления. Не делайте вывод о цене одного ролика по общему изменению кошелька: параллельно могли работать другие заказы. Сверять нужно связанные записи конкретного задания.
Внутренний лимит бюджета проверяйте с учётом одновременно принятых заказов. Десять отдельных запросов могут каждый выглядеть допустимыми, но вместе превысить желаемую сумму. Сначала ограничьте параллельность, затем добавляйте собственный учёт зарезервированного бюджета. Предварительная оценка всё равно не становится гарантией окончательной цены.
Сохраните результат так, чтобы он пережил истечение ссылки
У завершённого задания есть files и исходный result. Тип в files не обязательно заполнен: возможны null и application/octet-stream. Для видео учитывайте MIME, расширение URL или file_name и структуру result по схеме модели. Не называйте любой полученный URL MP4 только потому, что заказ относился к видео.
Подписанная ссылка может перестать действовать. Сохраняйте ID задания и при необходимости запрашивайте свежий ответ. Для долговременной выдачи клиенту скопируйте готовый файл в своё хранилище после проверки адреса, размера и формата. Ключ Gate отправляйте только API Gate, а не произвольному CDN из результата.
В карточке заказа полезны три простых действия: посмотреть результат, скачать разрешённый файл и скопировать запрос без секретов. Это дополняет существующий чат или личный кабинет. Отдельная сложная мастерская не требуется для архитектуры очереди: технические состояния и финансовые данные можно показывать рядом с привычным сообщением.
Проверьте архитектуру на сбоях ещё до массового запуска
Возьмите один небольшой заказ и выполните приёмку по наблюдаемым условиям. Два нажатия не должны давать два независимых ключа. Перезагрузка страницы должна открывать ту же запись. Перезапуск обработчика должен продолжать GET по сохранённому ID. После completed в карточке должны совпасть модель, файл и окончательное списание конкретного задания.
Отдельно проиграйте потерю ответа создания на имитации API. У вас останется тело и ключ, но не ID: восстановление обязано использовать прежнюю пару. Затем измените prompt с прежним ключом и убедитесь, что приложение останавливается до новой отправки. Такие локальные проверки помогают найти ошибки без расхода на видеогенерацию.
Проверьте ещё недоступную модель, пустой файл результата, неизвестный MIME и истёкшую ссылку. У каждого состояния должно быть понятное объяснение и сохранённые данные для разбора. После этого достаточно одного осмысленного платного теста нужного режима; он проверяет реальное качество и учёт, а не заменяет архитектурные проверки.
Частые вопросы
Нужен ли отдельный сервер для каждой видеомодели?
Нет. Один ваш backend может обслуживать несколько контрактов Gate и сохранять общий формат заказа. Различия входных схем обрабатываются адаптерами конкретных моделей.
Можно ли ждать генерацию прямо в браузере?
Показывать ожидание можно, но ключ и долговременное состояние должны оставаться на сервере. Закрытая вкладка не должна уничтожать сведения о принятом заказе.
Что делать, если ответ POST потерялся?
Сохранить прежнее тело и Idempotency-Key. Опросить известный ID либо восстановить исходный POST с тем же ключом; новый ключ способен создать ещё одну платную работу.
Когда считать цену окончательной?
После подтверждения завершённого задания, читая billing.cost_rub. Оценка и текущий резерв хранятся отдельно от фактического списания.