Зарегистрируйте mediaWebhookUrl, получайте POST по завершении задания, опрашивайте GET /api/media/jobs/{id}, обрабатывайте коды ошибок и учитывайте ограничения по списанию и идемпотентности.
Практическое руководство по асинхронным медиазадачам: регистрация вебхуков, статусы, опрос, коды ошибок, списания и лимиты идемпотентности — строго по текущей реализации в коде.
Жизненный цикл задания
POST /api/media/generate — при успехе 202 и { ok: true, jobId, status: "processing" }. Отдельных статусов queued / running в API нет: пока задача не завершена, клиент видит processing.
Вышестоящий сервис может проходить внутренние стадии (waiting, generating) — они не отражаются в публичном API; во время опроса задание остаётся processing.
Терминальные статусы: success (есть resultUrls, списание при успехе) или fail (без списания).
Завершение фиксируется один раз: через обратный вызов от upstream или при отложенном опросе в GET /api/media/jobs/{id}.
Опрос: GET /api/media/jobs/{id}
GEThttps://getvibeapi.com/api/media/jobs/{id}
Параметр
Тип
Описание
status
string
submitting | processing | success | fail. submitting + media_submission_uncertain is non-terminal; within about 10–15 minutes such a submission is closed as fail (media_submission_not_started) without a charge — create a new task.
Сумма списания в USD (0 при ошибке или до завершения).
failMsg
string | null
Безопасное сообщение при сбое или нетерминальном media_submission_uncertain.
errorCode
string | null
Код ошибки (media_generation_failed) или нетерминальное media_submission_uncertain.
retryGuidance
string | null
При media_submission_uncertain: безопасная инструкция — опросить тот же jobId позже и не создавать дубликат автоматически.
# 2. poll the job until it is ready
curl https://getvibeapi.com/api/media/jobs/JOB_ID \
-H "Authorization: Bearer $GETVIBE_API_KEY"
# → {"status":"success","resultUrls":["https://..."],"priceRub":...}
Рекомендуемый интервал опроса: 3–5 с с экспоненциальной задержкой до 30 с. Таймаут на стороне клиента — согласно SLA вашего приложения (генерация видео может занимать минуты).
Регистрация webhook
GEThttps://getvibeapi.com/api/webhooks
PATCHhttps://getvibeapi.com/api/webhooks
Один URL на аккаунт: mediaWebhookUrl. Настраивается в панели → Webhooks или через API с сессией аккаунта (cookie после входа). Эндпоинт не принимаетsk-pv-… — только аутентифицированную сессию.
# Example (a dashboard session is required, not an API key)
curl -X PATCH https://getvibeapi.com/api/webhooks \
-H "Content-Type: application/json" \
-b "session=YOUR_SESSION" \
-d '{"mediaWebhookUrl":"https://example.com/webhooks/media"}'
# Clear the URL
curl -X PATCH https://getvibeapi.com/api/webhooks \
-H "Content-Type: application/json" \
-b "session=YOUR_SESSION" \
-d '{"mediaWebhookUrl":null}'
Требования к URL
Параметр
Тип
Описание
mediaWebhookUrl
HTTPS URL
Публичный HTTPS-адрес; частные адреса и адреса loopback, а также наши собственные домены отклоняются (защита от SSRF), и адрес повторно проверяется перед каждой доставкой. Пустая строка или null сбрасывает его.
balanceWebhookUrl
HTTPS URL
Отдельный webhook для низкого баланса (не для media), подписанный тем же секретом аккаунта, с x-pv-event: balance.low. Пороговое значение — balanceWebhookThresholdRub.
Доставка webhook по завершении задачи
Когда задача достигает терминального статуса, GetVibe создаёт одно логическое событие и доставляет его через POST на зарегистрированный mediaWebhookUrl. Доставка ограничена: сервис делает до 5 попыток суммарно. Один и тот же payload может прийти более одного раза.
// POST to your mediaWebhookUrl when a job finishes
{
"jobId": "...",
"status": "success", // or "fail"
"model": "veo-3.1",
"kind": "video", // image | video | audio | music | 3d
"resultUrls": ["https://getvibeapi.com/api/media/file/..."],
"failMsg": null,
"createdAt": "2026-06-17T10:00:00.000Z"
}
Поведение доставки (как в коде)
Параметр
Тип
Описание
Signature / auth
—
Каждая доставка подписывается с помощью Standard Webhooks: заголовков webhook-id, webhook-timestamp и webhook-signature (v1, base64 HMAC-SHA256 от «id.timestamp.body»). Секрет аккаунта отображается в панели управления → Webhooks и возвращается для API-ключа в поле accountSecret при GET /api/media/webhook-secret; доставки на webhook_url, указанный для отдельного запроса, подписываются секретом этого ключа (поле secret). Отклоняйте запрос с неверной подписью или с временной меткой, которая старше 5 минут.
Retries
outbox
Всего до 5 попыток; интервал после сбоя: 1, 2, 4, 8 минут. После исчерпания попыток событие переходит в FAILED и больше не доставляется автоматически. Источником состояния остаётся опрос GET /api/media/jobs/{id}.
Receiver dedup
required
Удаляйте дубликаты по паре jobId + status и делайте обработчик идемпотентным. Не используйте вебхук как единственный источник состояния.
Content-Type
application/json
Тело — JSON с полями jobId, status, model, kind, resultUrls, failMsg, createdAt.
Ошибки POST /api/media/generate
HTTP
Когда
Списание
Retry
202
Задание принято (ok: true, jobId)
Нет (только после успеха)
—
400
Некорректный JSON / тело запроса
Нет
Исправьте запрос
401
Отсутствует или недействителен API-ключ
Нет
Проверить Authorization
402
Недостаточно средств (admission)
Нет
Пополните баланс
422
Неизвестная модель, недопустимые opts, ok: false от фасада
priceUnavailable, routing guard, VIDEO_DISABLED, veo without price
Нет
Другая модель / позже
Ошибки опроса и окончательные сбои
HTTP / статус
Значение
Списание
404
задача не найдена или чужой jobId
Нет
processing
Задача всё ещё выполняется
Нет
success
Готово, resultUrls заполнены
Да — priceRub в ответе
fail
Ошибка генерации
No (priceRub = 0)
Биллинг и идемпотентность
Списание один раз при успехе. В finalizeJob переход processing → success|fail атомарен: повторный callback или опрос не создаёт второго списания.
fail — бесплатно. При статусе fail списание не производится.
Квота при допуске. Если при создании была сохранена квота, при успехе списывается ровно она (никогда не больше фактического метеринга у провайдера).
Идемпотентность создания — через заголовок. Повторный POST на /api/media/generate без этого заголовка создаёт новую задачу и новое списание при успехе. Передайте Idempotency-Key (до 255 ASCII-символов): повтор с тем же ключом и телом в течение 24 часов вернёт тот же jobId без создания новой задачи; тот же ключ с другим телом — 422, а 409 — пока первый запрос ещё обрабатывается. jobId платной генерации также показан в GET /v1/generations (поле job_id).
Webhook ограничен пятью попытками. На каждый терминальный переход создаётся одно логическое событие, но попытки доставки могут повторяться. После пяти неудач событие получает статус FAILED. Обработчик на стороне клиента должен дедуплицировать пару jobId + status.