Vidtreo
Plataforma

Webhooks

Recibe notificaciones HTTP firmadas cuando se completan eventos de la plataforma — anatomía del payload, verificación HMAC y garantías de entrega.

Webhooks

Los webhooks envían eventos de la plataforma a tus sistemas en el momento en que ocurren, así nunca necesitas hacer polling para conocer el estado. Cada entrega es una solicitud POST con un cuerpo JSON, firmada con HMAC-SHA256 para que puedas verificar que proviene de VIDTREO.

Eventos Disponibles

EventoSe dispara cuando
transcriptions.transcription.completedTranscripción con IA termina de procesar un video

El catálogo de eventos sigue creciendo. El dashboard siempre muestra la lista actual de eventos a los que puedes suscribirte al crear o editar un endpoint.

Crear un Endpoint

Los endpoints de webhook se gestionan por entorno en el dashboard:

Inicia sesión en el Dashboard de VIDTREO

Abre tu entorno y ve a Configuración → Webhooks

Haz clic en Agregar Endpoint, ingresa tu URL https:// y selecciona los eventos a los que quieres suscribirte

Copia el secreto de firma (whsec_...) que se muestra después de la creación

El secreto de firma se muestra una sola vez. Se almacena cifrado y nunca puede recuperarse de nuevo: si lo pierdes, rótalo desde la configuración del endpoint para obtener uno nuevo.

Encabezados de Entrega

Cada entrega incluye encabezados de identificación:

EncabezadoContenido
X-Webhook-Signaturesha256=<hex> — HMAC-SHA256 del cuerpo de la solicitud sin procesar
X-Vidtreo-Event-IdID único del evento: úsalo para deduplicar el procesamiento
X-Vidtreo-Event-TypeEl nombre del evento, por ejemplo transcriptions.transcription.completed
X-Vidtreo-Event-VersionVersión del esquema del payload
X-Vidtreo-Webhook-Endpoint-IdEl ID del endpoint receptor
X-Vidtreo-Webhook-Delivery-IdEste intento de entrega específico: rastreable en el dashboard

Payload: transcriptions.transcription.completed

{
  "id": "9f4c1a2e-8d3b-4f6a-b1c9-2e7d5a8f0c3b",
  "event": "transcriptions.transcription.completed",
  "eventVersion": "2",
  "occurredAt": "2026-08-03T14:22:07.000Z",
  "data": {
    "transcription": {
      "id": "b7e2...",
      "videoId": "vid_...",
      "environmentId": "env_...",
      "language": "en",
      "wordCount": 812,
      "duration": 294.5
    },
    "video": {
      "id": "vid_...",
      "publicId": "pub_...",
      "filename": "interview-final.mp4",
      "duration": 295,
      "status": "COMPLETED",
      "metadata": { },
      "userMetadata": {
        "candidateId": "cand_8231",
        "jobId": "job_114"
      }
    },
    "files": [
      { "format": "vtt",  "url": "https://...", "expiresAt": "...", "contentType": "text/vtt" },
      { "format": "json", "url": "https://...", "expiresAt": "...", "contentType": "application/json" },
      { "format": "txt",  "url": "https://...", "expiresAt": "...", "contentType": "text/plain" }
    ]
  }
}

Dos campos merecen atención especial:

  • data.video.userMetadata — cualquier JSON que hayas adjuntado al video en el momento de subirlo, devuelto tal cual. Adjunta tus propios identificadores (ID de candidato, ID de curso, número de ticket) y tu handler nunca necesitará una tabla de referencia para saber a qué registro pertenece el video.
  • data.files[].url — URLs prefirmadas que expiran 5 minutos después de generarse la entrega. Descarga lo que necesites en cuanto llegue el evento; obtén URLs nuevas más adelante mediante GET /api/v1/videos/{videoId}/transcription.

Verificar Firmas

Verifica siempre el encabezado X-Webhook-Signature antes de confiar en un payload. La firma es un HMAC-SHA256 del cuerpo de la solicitud sin procesar, codificado en hexadecimal, calculado con el secreto de firma de tu endpoint:

import { createHmac, timingSafeEqual } from 'node:crypto'

function isFromVidtreo(rawBody: string, signature: string, secret: string) {
  const expected = `sha256=${createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')}`

  const a = Buffer.from(signature)
  const b = Buffer.from(expected)
  return a.length === b.length && timingSafeEqual(a, b)
}

Dos reglas que evitan los errores clásicos:

  1. Calcula el HMAC sobre el string del cuerpo sin procesar, antes de que cualquier parseo o re-serialización de JSON lo modifique.
  2. Compara con una función de tiempo constante (timingSafeEqual), nunca con ===.

Un handler mínimo:

app.post('/webhooks/vidtreo', async (req) => {
  if (!isFromVidtreo(req.rawBody, req.headers['x-webhook-signature'], process.env.VIDTREO_WEBHOOK_SECRET)) {
    return res.status(401).end()
  }

  const { video, files } = req.body.data
  const { candidateId } = video.userMetadata

  const txt = files.find(f => f.format === 'txt')
  const transcript = await fetch(txt.url).then(r => r.text())

  await processTranscript(candidateId, transcript)
  return res.status(200).end()
})

Responde rápido con un estado 2xx: realiza el procesamiento pesado después de confirmar la recepción, o de forma asíncrona.

Ciclo de Vida del Secreto

  • Los secretos usan el prefijo whsec_ y se generan del lado del servidor; también puedes proporcionar el tuyo al crear el endpoint
  • Se almacenan cifrados en reposo y se muestran en texto plano solo durante la creación y la rotación
  • Puedes rotarlos en cualquier momento desde la configuración del endpoint en el dashboard: actualiza el secreto de tu handler cuando lo hagas

Garantías de Entrega

  • Reintentos automáticos — el procesamiento fallido se reintenta con un retraso antes de desistir
  • Captura dead-letter — los eventos que agotan los reintentos se conservan, no se descartan
  • Historial completo de entregas — cada intento, código de respuesta y payload se puede inspeccionar por endpoint en el dashboard
  • Reenvío manual — puedes volver a enviar un solo evento, una sola entrega o todas las entregas fallidas dentro de una ventana de tiempo, desde el dashboard

Usa X-Vidtreo-Event-Id para que tu handler sea idempotente: un evento reenviado lleva el mismo ID de evento que el original.

En esta página