Webhooks: eventos, payloads y firmas

Los webhooks permiten que tus propios sistemas reaccionen en cuanto Inwista termina el trabajo: cuando se completa una transcripción o se renderiza una exportación de video, Inwista hace un POST de un evento JSON firmado a un endpoint HTTPS que tú controlas. Úsalo para alimentar canalizaciones de publicación, sistemas de archivo, gestores de proyectos — cualquier cosa que deba ocurrir automáticamente cuando Inwista acaba.

Configuración y el secreto de firma

En My workspaceIntegrations, conecta la tarjeta de Webhooks e introduce la URL de tu endpoint (HTTPS obligatorio). Inwista genera un secreto de firma con el prefijo whsec_ y lo muestra exactamente una vez — guárdalo en tu gestor de secretos de inmediato. Puedes rotar el secreto en cualquier momento desde los ajustes de la integración; el secreto antiguo deja de funcionar al instante.

Usa Send test event en los ajustes para disparar un evento webhook.test firmado contra tu endpoint y confirmar el cableado antes de cualquier tráfico real. Cambiar la URL del endpoint más adelante conserva el mismo secreto.

Eventos

  • transcript.completed Se dispara una vez por proyecto, cuando termina la transcripción inicial. Lleva enlaces de descarga de los archivos de transcripción en los formatos configurados en la integración.
  • export.completed Se dispara cada vez que termina un render de video. Lleva el video renderizado más archivos de transcripción frescos que reflejan cualquier edición de subtítulos hecha desde la transcripción.
  • webhook.test Se dispara manualmente desde los ajustes de la integración, para verificar tu receptor.

Ambos tipos de evento se activan o desactivan de forma independiente en los ajustes de la integración. Los eventos se disparan en cada ocurrencia suscrita con independencia de las selecciones de almacenamiento por exportación — los webhooks son notificaciones, no destinos de entrega.

Payload

Todos los eventos comparten el mismo sobre: un tipo de event, un id único para deduplicación, un timestamp Unix, el workspaceId y un bloque project. Los eventos con archivos añaden un array files; export.completed incluye además un objeto video.

{
  "event": "export.completed",
  "id": "evt_9c1b7e2a4f0d4b6e8a12",
  "timestamp": 1784034017,
  "workspaceId": "UK6naH1L1sMtzTVcVZih",
  "project": {
    "id": "abc123def456",
    "name": "Interview — Episode 12",
    "language": "no",
    "durationSeconds": 1834
  },
  "files": [
    {
      "format": "srt",
      "name": "Interview — Episode 12.srt",
      "mimeType": "application/x-subrip",
      "url": "https://storage.googleapis.com/...signed...",
      "expiresAt": 1784120417
    }
  ],
  "video": {
    "name": "Interview — Episode 12.mp4",
    "mimeType": "video/mp4",
    "sizeBytes": 812340221,
    "url": "https://storage.googleapis.com/...signed...",
    "expiresAt": 1784120417
  }
}

Los valores de url son enlaces de descarga firmados válidos durante 24 horas (el timestamp Unix de expiresAt dice exactamente cuándo) — descarga lo que necesites pronto en lugar de guardar los enlaces. transcript.completed tiene la misma forma sin el objeto video.

Verificar las firmas

Cada petición lleva tres cabeceras: X-Inwista-Event (el tipo de evento), X-Inwista-Delivery (el id del evento) y X-Inwista-Signature — un HMAC-SHA256 del cuerpo bruto de la petición, calculado con tu secreto de firma. Recalcúlalo y compáralo antes de confiar en el payload; sin esta comprobación, cualquiera que descubra la URL de tu endpoint podría enviarte eventos falsos.

const crypto = require("crypto");

function verifyInwistaSignature(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return (
    signatureHeader.length === expected.length &&
    crypto.timingSafeEqual(
      Buffer.from(signatureHeader),
      Buffer.from(expected),
    )
  );
}

// Express example — capture the raw body for verification:
// app.post("/inwista-hook", express.raw({ type: "application/json" }), (req, res) => {
//   if (!verifyInwistaSignature(req.body, req.get("X-Inwista-Signature"), process.env.INWISTA_WEBHOOK_SECRET)) {
//     return res.status(401).end();
//   }
//   const event = JSON.parse(req.body);
//   res.status(200).end(); // acknowledge fast, process async
// });

Importante: calcula el HMAC sobre el cuerpo bruto de la petición, no sobre una versión reserializada del JSON parseado — las diferencias de orden de claves y espacios romperán la comparación.

Entrega, reintentos y deduplicación

  • Confirma con un 2xx Responde en menos de 10 segundos. Haz el procesamiento pesado de forma asíncrona después de confirmar — una respuesta lenta cuenta como fallo.
  • Reintentos Las entregas fallidas se reintentan hasta dos veces más (aproximadamente 10 y 30 segundos después). Las respuestas del rango 4xx se tratan como permanentes y no se reintentan, salvo 408 y 429.
  • Deduplica por id Si tu servidor acepta una petición pero la respuesta se pierde, un reintento puede entregar el mismo evento dos veces. El campo id es estable entre reintentos — úsalo para ignorar duplicados.
  • Pausa automática Tras 10 entregas fallidas consecutivas, Inwista pausa el endpoint y muestra el motivo en los ajustes de la integración, donde puedes reanudarlo una vez arreglado tu receptor. El estado y código HTTP de la última entrega siempre son visibles ahí.