Skip to Content

Webhooks

Pasa un callback_url cuando creas una tarea y ofox entrega el resultado por webhook en lugar de que tengas que consultarlo. Cuando la tarea llega a un estado terminal, ofox envía un POST a tu URL con el objeto completo de la tarea en el cuerpo (una entrega fallida se reintenta — consulta la sección Reintentos más abajo).

callback_url debe usar HTTPS y pasar la validación SSRF (se rechazan direcciones privadas, de bucle local y de metadatos cloud). Una URL inválida se rechaza al crear la tarea con 400 invalid_callback_url; ver Crear video.

Eventos

Se entrega un evento por tarea, correspondiente al estado terminal en el que terminó (el mismo valor aparece en data.status).

EventoSe activa cuando
video.generation.completedLa tarea tuvo éxito
video.generation.failedLa tarea falló
video.generation.cancelledLa tarea fue cancelada
video.generation.expiredLa tarea expiró

Payload de entrega

Cada entrega es un POST a tu callback_url con estos headers:

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>

El cuerpo es un sobre. Su campo data es idéntico al cuerpo de respuesta de Obtener estado del video para la misma tarea:

{ "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 } }
CampoTipoDescripción
eventstringUno de los tipos de evento anteriores
idstringId de tarea (UUID)
created_atintegerMarca de tiempo del evento, segundos Unix
dataobjectEl objeto completo de la tarea: misma forma que GET /v1/videos/{id}

Idempotencia

El mismo evento puede entregarse más de una vez: un reintento puede dispararse después de que tu endpoint ya procesó la primera entrega. Deduplica usando el header X-Ofox-Idempotency-Key, que es <id>-<status> (por ejemplo 9bcf3c60-7db2-4e1a-a1b2-c3d4e5f60718-completed), y procesa cada key como máximo una vez.

Responde con cualquier estado 2xx para confirmar la recepción. Una respuesta que no sea 2xx o un timeout se trata como entrega fallida y dispara un reintento, así que devuelve 2xx rápidamente y realiza cualquier trabajo pesado de forma asíncrona.

Verificación de firma

Cada entrega incluye un header X-Ofox-Signature: HMAC-SHA256(raw request body, your webhook secret), codificado en base64url. Vuelve a calcularlo con tu secreto y compáralo en tiempo constante antes de confiar en el 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) }

Calcula el HMAC sobre los bytes sin procesar exactamente como se recibieron, antes de cualquier parseo JSON o serialización de nuevo; de lo contrario, la firma no coincidirá. En Express, captura el cuerpo con express.raw({ type: 'application/json' }).

Reintentos

Una entrega fallida (no 2xx o timeout) se reintenta automáticamente con backoff exponencial limitado más jitter: hasta 10 reintentos durante una ventana de aproximadamente 40 minutos. La demora antes del reintento n es min(5·2^(n-1), 600) segundos, más jitter aleatorio.

ReintentoDemoraAcumulado
15s5s
210s15s
320s35s
440s1m15s
580s2m35s
6160s5m15s
7320s10m35s
8600s (limitado)20m35s
9600s30m35s
10600s40m35s

Fallback

Si se agotan todos los reintentos, ofox deja de intentar la entrega. El resultado de la tarea nunca se pierde: siempre puedes consultar GET /v1/videos/{id} para obtener el estado final (sujeto al límite de tasa de ese endpoint).

Last updated on