Тема
Язык

Webhooks и жизненный цикл медиазадач

Зарегистрируйте mediaWebhookUrl, получайте POST по завершении задания, опрашивайте GET /api/media/jobs/{id}, обрабатывайте коды ошибок и учитывайте ограничения по списанию и идемпотентности.

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

Жизненный цикл задания

  1. POST /api/media/generate — при успехе 202 и { ok: true, jobId, status: "processing" }. Отдельных статусов queued / running в API нет: пока задача не завершена, клиент видит processing.
  2. Вышестоящий сервис может проходить внутренние стадии (waiting, generating) — они не отражаются в публичном API; во время опроса задание остаётся processing.
  3. Терминальные статусы: success (есть resultUrls, списание при успехе) или fail (без списания).
  4. Завершение фиксируется один раз: через обратный вызов от upstream или при отложенном опросе в GET /api/media/jobs/{id}.

Опрос: GET /api/media/jobs/{id}

GEThttps://getvibeapi.com/api/media/jobs/{id}
ПараметрТипОписание
statusstringsubmitting | 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.
resultUrlsstring[]Подписанные URL /api/media/file/{id}/{idx}?sig=… (без API-ключа).
priceRubnumberСумма списания в USD (0 при ошибке или до завершения).
failMsgstring | nullБезопасное сообщение при сбое или нетерминальном media_submission_uncertain.
errorCodestring | nullКод ошибки (media_generation_failed) или нетерминальное media_submission_uncertain.
retryGuidancestring | 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
ПараметрТипОписание
mediaWebhookUrlHTTPS URLПубличный HTTPS-адрес; частные адреса и адреса loopback, а также наши собственные домены отклоняются (защита от SSRF), и адрес повторно проверяется перед каждой доставкой. Пустая строка или null сбрасывает его.
balanceWebhookUrlHTTPS 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 минут.
RetriesoutboxВсего до 5 попыток; интервал после сбоя: 1, 2, 4, 8 минут. После исчерпания попыток событие переходит в FAILED и больше не доставляется автоматически. Источником состояния остаётся опрос GET /api/media/jobs/{id}.
Receiver deduprequiredУдаляйте дубликаты по паре jobId + status и делайте обработчик идемпотентным. Не используйте вебхук как единственный источник состояния.
Content-Typeapplication/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 от фасадаНетFix model/opts
502media_submit_failed — вышестоящий сервис недоступенНетRetry с backoff
503priceUnavailable, 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.
См. также: Генерация медиа, Каталог медиамоделей, Ошибки и лимиты (распространённые коды 401/402/429 для /v1).
Вебхуки медиа в GetVibe API — статусы, ошибки, биллинг | GetVibe API