Pular para o conteúdo
int8labs

Webhooks

Quando uma transcrição em lote termina, mandamos um POST para a sua webhook_url, assinado com o segredo da organização. Assim você não precisa ficar consultando o job.

2 min de leitura · atualizado em 13/10/2026

Eventos

  • transcription.completed: o job concluiu; data.result traz o texto e os trechos.
  • transcription.failed: o job falhou; data.error traz o code e a message. Nada foi cobrado.

O corpo é um JSON compacto com o type e o job inteiro em data, na mesma forma de GET /v1/stt/transcriptions/{id}:

{
"type": "transcription.completed",
"data": {
"id": "0f8e2a6c-3b1d-4c9e-8a7f-5d2b1e9c4a60",
"status": "completed",
"source": "api",
"input": "upload",
"file_name": "consulta.m4a",
"size_bytes": 24117248,
"duration_ms": 2538000,
"keyterms": [
"Dra. Helena Prado",
"losartana"
],
"webhook_url": "https://api.suaempresa.com.br/int8",
"cost_brl": 0.07755011,
"error": null,
"result": {
"text": "bom dia dona ana paula tudo bem com a senhora trouxe sim doutora o de sangue e o eletrocardiograma",
"segments": [
{
"start_ms": 0,
"end_ms": 14120,
"text": "bom dia dona ana paula tudo bem com a senhora"
},
{
"start_ms": 14120,
"end_ms": 31480,
"text": "trouxe sim doutora o de sangue e o eletrocardiograma"
}
],
"duration_ms": 2538000
},
"created_at": "2026-10-13T14:30:02.118Z",
"started_at": "2026-10-13T14:30:02.540Z",
"completed_at": "2026-10-13T14:31:10.902Z",
"expires_at": "2026-11-12T14:31:10.902Z"
}
}

Verificar a assinatura

Todo envio leva o cabeçalho Int8-Signature: t=<unix>,v1=<hex>. O v1 é o HMAC-SHA256, em hexadecimal, de t, um ponto e o corpo cru, com o segredo inteiro da organização (whsec_…) como chave. O segredo fica em console › Configurações › Webhooks.

import hmac, hashlib, time
​
def verificar(corpo: bytes, cabecalho: str, segredo: str) -> bool:
try:
partes = dict(p.split("=", 1) for p in cabecalho.split(","))
t, v1 = partes["t"], partes["v1"]
if abs(time.time() - int(t)) > 300:
return False # mais de 5 min: recuse
base = t.encode() + b"." + corpo
esperado = hmac.new(segredo.encode(), base, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado.encode(), v1.encode())
except (AttributeError, KeyError, ValueError, OverflowError):
return False # cabeçalho ausente ou malformado: recuse
​
# Int8-Signature: t=1791937912,v1=5f2c9a…e81d

Confira três coisas: a assinatura bate, o t tem menos de 5 minutos (contra replay) e a comparação é em tempo constante. Use o corpo como chegou, antes de qualquer parse: reformatar o JSON muda os bytes e a assinatura.

Um servidor completo

import json
import os
​
from flask import Flask, abort, request
​
from verificacao import verificar # a função de verificação acima
​
app = Flask(__name__)
SEGREDO = os.environ["INT8_WEBHOOK_SECRET"] # whsec_… de console › Configurações › Webhooks
​
​
@app.post("/int8")
def webhook_int8():
corpo = request.get_data() # os bytes crus, como foram assinados
try:
valido = verificar(corpo, request.headers["Int8-Signature"], SEGREDO)
except (KeyError, ValueError):
valido = False
if not valido:
abort(400)
evento = json.loads(corpo)
job = evento["data"]
if evento["type"] == "transcription.completed":
salvar_transcricao(job["id"], job["result"]["text"])
elif evento["type"] == "transcription.failed":
avisar_falha(job["id"], job["error"]["code"])
return "", 204
​
​
def salvar_transcricao(job_id: str, texto: str) -> None:
...
​
​
def avisar_falha(job_id: str, codigo: str) -> None:
...

Tentativas

Até 3 tentativas: a primeira sai na hora; a segunda, 30 s depois da primeira falhar; a terceira, 5 min depois da segunda falhar. Vale a primeira resposta 2xx. As tentativas pendentes ficam na memória da API: se ela reiniciar no meio, as que faltavam se perdem (o job continua consultável com GET). Cada tentativa é assinada de novo, com o t da hora. Responda rápido (em poucos segundos) e processe depois: uma fila do seu lado evita tentativa repetida por demora.

O mesmo evento pode chegar mais de uma vez (por exemplo, se a sua resposta se perder). Use o data.id do job para não processar duas vezes.

Rotacionar o segredo

Em console › Configurações › Webhooks, "Rotacionar" gera um segredo novo na hora; o anterior para de valer. Atualize o seu servidor logo em seguida.