Vidtreo
Plataforma

Webhooks

Receba notificações HTTP assinadas quando eventos da plataforma forem concluídos — anatomia do payload, verificação HMAC e garantias de entrega.

Webhooks

Webhooks enviam eventos da plataforma para seus sistemas conforme acontecem, então você nunca precisa fazer polling de status. Cada entrega é uma requisição POST com um corpo JSON, assinada com HMAC-SHA256 para que você possa verificar que veio da VIDTREO.

Eventos Disponíveis

EventoDispara quando
transcriptions.transcription.completedTranscrição por IA termina de processar um vídeo

O catálogo de eventos está crescendo. O dashboard sempre mostra a lista atual de eventos aos quais você pode se inscrever ao criar ou editar um endpoint.

Criando um Endpoint

Endpoints de webhook são gerenciados por ambiente no dashboard:

Faça login no VIDTREO Dashboard

Abra seu ambiente e vá para Configurações → Webhooks

Clique em Adicionar Endpoint, insira sua URL https:// e selecione os eventos aos quais deseja se inscrever

Copie o segredo de assinatura (whsec_...) exibido após a criação

O segredo de assinatura é exibido uma vez. Ele é armazenado de forma criptografada e nunca pode ser recuperado novamente — se você perdê-lo, rotacione-o nas configurações do endpoint para obter um novo.

Cabeçalhos de Entrega

Toda entrega inclui cabeçalhos de identificação:

CabeçalhoConteúdo
X-Webhook-Signaturesha256=<hex> — HMAC-SHA256 do corpo bruto da requisição
X-Vidtreo-Event-IdID único do evento — use-o para deduplicar o processamento
X-Vidtreo-Event-TypeO nome do evento, ex.: transcriptions.transcription.completed
X-Vidtreo-Event-VersionVersão do schema do payload
X-Vidtreo-Webhook-Endpoint-IdO ID do endpoint que recebe o evento
X-Vidtreo-Webhook-Delivery-IdEsta tentativa de entrega específica — rastreável no 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" }
    ]
  }
}

Dois campos merecem atenção especial:

  • data.video.userMetadata — qualquer JSON que você anexou ao vídeo no momento do upload, retornado literalmente. Anexe seus próprios identificadores (ID do candidato, ID do curso, número do ticket) e seu handler nunca precisará de uma tabela de referência para saber a qual registro o vídeo pertence.
  • data.files[].url — URLs pré-assinadas que expiram em 5 minutos após a entrega ser gerada. Baixe o que você precisar assim que o evento chegar; busque novas URLs depois via GET /api/v1/videos/{videoId}/transcription.

Verificando Assinaturas

Sempre verifique o cabeçalho X-Webhook-Signature antes de confiar em um payload. A assinatura é um HMAC-SHA256 do corpo bruto da requisição, codificado em hex, calculado com o segredo de assinatura do seu 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)
}

Duas regras que evitam os erros clássicos:

  1. Calcule o HMAC sobre a string bruta do corpo, antes que qualquer parsing ou re-serialização de JSON a toque.
  2. Compare usando uma função de tempo constante (timingSafeEqual), nunca ===.

Um 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()
})

Responda com um status 2xx rapidamente — faça o processamento pesado depois de confirmar o recebimento, ou de forma assíncrona.

Ciclo de Vida do Segredo

  • Os segredos usam o prefixo whsec_ e são gerados no servidor; você também pode fornecer o seu próprio ao criar o endpoint
  • Armazenados de forma criptografada em repouso, exibidos em texto plano apenas na criação e na rotação
  • Rotacione a qualquer momento nas configurações do endpoint no dashboard — atualize o segredo do seu handler quando fizer isso

Garantias de Entrega

  • Retentativas automáticas — o processamento com falha é retentado com um atraso antes de desistir
  • Captura dead-letter — eventos que esgotam as retentativas são preservados, não descartados
  • Histórico completo de entregas — toda tentativa, código de resposta e payload é inspecionável por endpoint no dashboard
  • Reenvio manual — reenvie (redrive) um único evento, uma única entrega, ou todas as entregas com falha em uma janela de tempo, a partir do dashboard

Use X-Vidtreo-Event-Id para tornar seu handler idempotente — um evento reenviado carrega o mesmo ID de evento que o original.

Nesta página