Skip to Content
APIГенерация видеоWebhook-уведомления

Webhooks

Передайте callback_url, когда создаете задачу, и ofox доставит результат через webhook вместо того, чтобы вы опрашивали его. Когда задача достигает конечного состояния, ofox отправляет POST на ваш URL с полным объектом задачи в body (неудачная доставка повторяется — см. раздел «Повторы» ниже).

callback_url должен быть HTTPS и проходить SSRF-проверку (приватные, loopback- и cloud-metadata-адреса отклоняются). Недействительный URL отклоняется при создании с 400 invalid_callback_url — см. Создание видео.

События

На одну задачу доставляется одно событие, соответствующее конечному статусу, в котором она остановилась (то же значение появляется в data.status).

СобытиеКогда срабатывает
video.generation.completedЗадача успешно завершилась
video.generation.failedЗадача завершилась ошибкой
video.generation.cancelledЗадача была отменена
video.generation.expiredЗадача истекла

Payload доставки

Каждая доставка — это POST на ваш callback_url с такими заголовками:

POST https://your-app.example/webhooks/ofox-video Content-Type: application/json X-Ofox-Idempotency-Key: 9bcf3c60-7db2-4e1a-a1b2-c3d4e5f60718-completed X-Ofox-Signature: <HMAC-SHA256, base64url>

Body представляет собой envelope. Его поле data идентично body ответа Получение статуса видео для той же задачи:

{ "event": "video.generation.completed", "id": "9bcf3c60-7db2-4e1a-a1b2-c3d4e5f60718", "created_at": 1776211400, "data": { "id": "9bcf3c60-7db2-4e1a-a1b2-c3d4e5f60718", "status": "completed", "model": "google/veo-3.1", "prompt": "A golden retriever running on the beach at sunset", "unsigned_urls": ["https://upstream.example/out.mp4"], "mirror_urls": ["https://cdn.ofox.ai/videos/vgen_xxx.mp4?sig=..."], "usage": { "video_seconds": 5, "video_cost": "0.4000000000" }, "created_at": 1776211362, "updated_at": 1776211400 } }
ПолеТипОписание
eventstringОдин из типов событий выше
idstringTask id (UUID)
created_atintegerВремя события, Unix seconds
dataobjectПолный объект задачи — та же форма, что и GET /v1/videos/{id}

Идемпотентность

Одно и то же событие может быть доставлено более одного раза — повтор может сработать после того, как ваш эндпоинт уже обработал первую доставку. Дедуплицируйте по заголовку X-Ofox-Idempotency-Key, который равен <id>-<status> (например, 9bcf3c60-7db2-4e1a-a1b2-c3d4e5f60718-completed), и обрабатывайте каждый ключ не более одного раза.

Ответьте любым статусом 2xx, чтобы подтвердить получение. Ответ не-2xx или таймаут считается неудачной доставкой и запускает повтор, поэтому возвращайте 2xx быстро, а любую тяжелую работу выполняйте асинхронно.

Проверка подписи

Каждая доставка содержит заголовок X-Ofox-Signature: HMAC-SHA256(raw request body, your webhook secret), закодированный base64url. Пересчитайте его со своим secret и сравните за постоянное время, прежде чем доверять payload.

verify-webhook.js
import crypto from 'node:crypto' // X-Ofox-Signature = HMAC-SHA256(raw request body, your webhook secret), // base64url-encoded. function verifyOfoxSignature(rawBody, signatureHeader, secret) { if (typeof signatureHeader !== 'string') return false const expected = crypto .createHmac('sha256', secret) .update(rawBody) .digest('base64url') const a = Buffer.from(expected) const b = Buffer.from(signatureHeader) // Constant-time compare; guard length first (timingSafeEqual throws on mismatch). return a.length === b.length && crypto.timingSafeEqual(a, b) }

Вычисляйте HMAC по сырым байтам ровно в том виде, в котором они получены, до любого JSON parse или повторной сериализации — иначе подпись не совпадет. В Express захватывайте body через express.raw({ type: 'application/json' }).

Повторы

Неудачная доставка (не-2xx или таймаут) автоматически повторяется с ограниченным экспоненциальным backoff и jitter — до 10 повторов в окне примерно 40 минут. Задержка перед повтором n равна min(5·2^(n-1), 600) секунд плюс случайный jitter.

ПовторЗадержкаНакопительно
15s5s
210s15s
320s35s
440s1m15s
580s2m35s
6160s5m15s
7320s10m35s
8600s (capped)20m35s
9600s30m35s
10600s40m35s

Fallback

Если все повторы исчерпаны, ofox прекращает попытки доставки. Результат задачи никогда не теряется — вы всегда можете опросить GET /v1/videos/{id}, чтобы получить финальное состояние (с учетом лимита запросов этого эндпоинта).

Last updated on