Registra un mediaWebhookUrl, recibe un POST cuando finaliza un trabajo, consulta GET /api/media/jobs/{id}, gestiona los códigos de error y comprende los límites de cobro e idempotencia.
Guía operativa para trabajos multimedia asíncronos: registro de webhooks, estados, consulta periódica, códigos de error, cargos y límites de idempotencia, estrictamente según la implementación actual del código.
Ciclo de vida del trabajo
POST /api/media/generate: cuando tiene éxito, 202 y { ok: true, jobId, status: "processing" }. No hay estados queued / running separados en la API: hasta que finaliza el trabajo, el cliente ve processing.
El servicio ascendente puede pasar por etapas internas (waiting, generating); estas no se reflejan en la API pública. Mientras haces sondeos, el trabajo permanece en processing.
Estados de Terminal: success (tiene resultUrls, se cobra si tiene éxito) o fail (sin cargo).
La finalización se registra una sola vez: mediante la devolución de llamada del upstream o en una consulta diferida en GET /api/media/jobs/{id}.
Consulta: GET /api/media/jobs/{id}
GEThttps://getvibeapi.com/api/media/jobs/{id}
Parámetro
Tipo
Descripción
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.
resultUrls
string[]
URL firmadas /api/media/file/{id}/{idx}?sig=… (sin clave de API).
priceRub
number
Importe del cargo en USD (0 si falla o antes de completarse).
failMsg
string | null
Mensaje seguro en caso de error o de un media_submission_uncertain no terminal.
errorCode
string | null
Código de error (media_generation_failed) o non-terminal media_submission_uncertain.
retryGuidance
string | null
En media_submission_uncertain: una instrucción segura para consultar más tarde el mismo jobId y no crear automáticamente un duplicado.
# 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":...}
Intervalo de sondeo recomendado: 3–5 s, con un retroceso exponencial de hasta 30 s. El tiempo de espera del lado del cliente depende del SLA de tu aplicación (la generación de video puede tardar minutos).
Registro de webhooks
GEThttps://getvibeapi.com/api/webhooks
PATCHhttps://getvibeapi.com/api/webhooks
Una URL por cuenta: mediaWebhookUrl. Se configura en dashboard → Webhooks o mediante la API con una sesión de cuenta (una cookie después de iniciar sesión). El endpoint no acepta sk-pv-…; solo una sesión autenticada.
# 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}'
Requisitos de URL
Parámetro
Tipo
Descripción
mediaWebhookUrl
URL HTTPS
Una dirección HTTPS pública; se rechazan las direcciones privadas o de loopback y nuestros propios dominios (protección contra SSRF), y la dirección se vuelve a comprobar antes de cada entrega. Una cadena vacía o null la restablece.
balanceWebhookUrl
URL HTTPS
Un webhook independiente para saldo bajo (no para contenido multimedia), firmado con el mismo secreto de la cuenta, x-pv-event: balance.low. El umbral es balanceWebhookThresholdRub.
Entrega del webhook al completar el trabajo
Cuando un trabajo alcanza un estado terminal, GetVibe crea un evento lógico y lo entrega mediante POST al mediaWebhookUrl registrado. La entrega está limitada: el servicio realiza un máximo de 5 intentos en total. El mismo contenido puede llegar más de una vez.
// 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"
}
Comportamiento de entrega (tal como está en el código)
Parámetro
Tipo
Descripción
Signature / auth
—
Cada entrega se firma con Standard Webhooks: los encabezados webhook-id, webhook-timestamp y webhook-signature (v1, HMAC-SHA256 en base64 de «id.timestamp.body»). El secreto de la cuenta se muestra en el panel → Webhooks y se devuelve a una clave de API como accountSecret mediante GET /api/media/webhook-secret; las entregas a un webhook_url por solicitud se firman con el secreto de esa clave (el campo secret). Rechaza las solicitudes con una firma incorrecta o una marca de tiempo de más de 5 minutos.
Retries
outbox
Hasta 5 intentos en total; intervalo después de un fallo: 1, 2, 4, 8 minutos. Después de agotar los intentos, el evento pasa a FAILED y deja de entregarse automáticamente. El sondeo de GET /api/media/jobs/{id} sigue siendo la fuente del estado.
Receiver dedup
required
Deduplica por el par jobId + status y haz que tu controlador sea idempotente. No uses el webhook como única fuente de estado.
Content-Type
application/json
El cuerpo es JSON con los campos jobId, status, model, kind, resultUrls, failMsg, createdAt.
Errores de POST /api/media/generate
HTTP
Cuándo
Cargo
Reintentar
202
Trabajo aceptado (ok: true, jobId)
No (solo después de una operación exitosa)
—
400
JSON / cuerpo de la solicitud no válidos
No
Corrige la solicitud
401
Falta la clave de API o no es válida
No
Comprobar Authorization
402
Saldo insuficiente (admisión)
No
Recargar el saldo
422
Modelo desconocido, opts no válidas, ok: false desde la fachada
No
Fix model/opts
502
media_submit_failed: upstream unavailable
No
Reintentar con retroceso
503
priceUnavailable, protección de enrutamiento, VIDEO_DISABLED, veo sin precio
No
Otro modelo / más adelante
Errores de consulta y fallos terminales
HTTP / estado
Significado
Cargo
404
trabajo no encontrado o un jobId ajeno
No
processing
La tarea todavía se está ejecutando
No
success
Listo, resultUrls rellenado
Sí: priceRub en la respuesta
fail
La generación falló
No (priceRub = 0)
Facturación e idempotencia
Se cobra una sola vez cuando tiene éxito. En finalizeJob, la transición processing → success|fail es atómica: un callback o una consulta repetidos no generan un segundo cobro.
fail — gratis. No se realiza ningún débito con un estado fail.
Cuota de admisión. Si se guardó una cuota al crearla, se cobra exactamente esa cantidad cuando la operación se completa correctamente (nunca más que la medición real del proveedor ascendente).
Crea la idempotencia mediante un header. Un POST repetido a /api/media/generate sin el header crea un nuevo trabajo y genera un nuevo cobro cuando tiene éxito. Envía Idempotency-Key (hasta 255 caracteres ASCII): un reintento con la misma clave y el mismo cuerpo durante 24 horas devuelve el mismo jobId sin crear un nuevo trabajo; la misma clave con un cuerpo diferente devuelve 422, y 409 mientras la primera solicitud siga procesándose. El jobId de una generación pagada también aparece en GET /v1/generations (el campo job_id).
El webhook está limitado a cinco intentos. Se crea un evento lógico por cada transición terminal, pero los intentos de entrega pueden repetirse. Después de cinco fallos, el evento recibe el estado FAILED. El controlador del cliente debe deduplicar el par jobId + status.