Webhooks
Um projeto pode ter um webhook: uma URL sua que recebe um POST quando um job do projeto termina. Com ele, o seu sistema não precisa consultar GET /v1/jobs/{id} até o fim.
Como ativar
Seção intitulada “Como ativar”Na página do projeto, em Webhook, um admin informa a URL e clica em Ativar. O segredo de assinatura aparece uma vez: guarde-o junto do seu sistema. Na mesma área dá para:
- Enviar teste: manda um evento
ping, para conferir o recebimento e a assinatura; - Trocar segredo: as entregas seguintes passam a usar o segredo novo, na hora;
- Remover: deixa de avisar, e as entregas na fila são descartadas;
- ver as últimas entregas, com o estado e a resposta do seu sistema.
A URL precisa ser https e pública: endereços de rede interna são recusados, na ativação e a cada envio. Redirecionamentos não são seguidos.
Os eventos
Seção intitulada “Os eventos”evento |
Quando |
|---|---|
job.concluido |
O job terminou e o resultado está pronto. |
job.falhou |
O job falhou; a reserva voltou inteira. |
job.cancelado |
O job foi cancelado antes de ganhar máquina; a reserva voltou. |
ping |
Você pediu um teste. |
O corpo é JSON. Nos eventos de job, job traz os mesmos campos de GET /v1/jobs/{id}:
{ "evento": "job.concluido", "criado_em": "2026-11-01T14:03:11.204Z", "job": { "id": "6b0d1c3e-5f1a-4c2b-9a77-0e4f2d9b8c11", "estado": "concluido", "tipo": "simulacao", "preco_micros": 14499640, "criado_em": "2026-11-01T13:40:02.511Z", "registro": { "…": "…" } }}Os cabeçalhos
Seção intitulada “Os cabeçalhos”| Cabeçalho | O que é |
|---|---|
BS-Webhook-Id |
O id da entrega. Uma entrega cuja resposta se perdeu é enviada de novo com o mesmo id: guarde os ids recebidos e ignore os repetidos. |
BS-Webhook-Timestamp |
O momento do envio, em segundos desde 1970 (UTC). |
BS-Webhook-Signature |
v1= seguido do HMAC-SHA256, em hexadecimal, de "<timestamp>.<corpo>" com o seu segredo. |
Confira a assinatura
Seção intitulada “Confira a assinatura”Calcule o HMAC sobre os bytes do corpo como chegaram, antes de interpretar o JSON, e compare em tempo constante. Recuse também um timestamp com mais de 5 minutos, para que uma entrega gravada não possa ser reenviada depois.
import hashlibimport hmacimport time
def assinatura_valida(segredo: str, corpo: bytes, cabecalhos: dict[str, str]) -> bool: timestamp = cabecalhos["BS-Webhook-Timestamp"] if abs(time.time() - int(timestamp)) > 300: return False esperado = hmac.new(segredo.encode(), f"{timestamp}.".encode() + corpo, hashlib.sha256) return hmac.compare_digest("v1=" + esperado.hexdigest(), cabecalhos["BS-Webhook-Signature"])import { createHmac, timingSafeEqual } from "node:crypto";
export function assinaturaValida(segredo: string, corpo: Buffer, cabecalhos: Record<string, string>): boolean { const timestamp = cabecalhos["bs-webhook-timestamp"]; if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; const esperado = "v1=" + createHmac("sha256", segredo).update(`${timestamp}.`).update(corpo).digest("hex"); const recebido = cabecalhos["bs-webhook-signature"] ?? ""; return esperado.length === recebido.length && timingSafeEqual(Buffer.from(esperado), Buffer.from(recebido));}Respostas e novas tentativas
Seção intitulada “Respostas e novas tentativas”- Responda
2xxrápido. A entrega espera até 10 segundos pela resposta. Faça o trabalho pesado depois de responder. - Qualquer outra resposta, ou nenhuma, conta como falha. A entrega é repetida depois de 1 minuto, 5 minutos, 30 minutos, 2 horas e 12 horas. Depois da sexta tentativa, desistimos dela.
- A ordem não é garantida, e um evento pode chegar mais de uma vez: use o
BS-Webhook-Idpara não processar duas vezes. - O webhook avisa, não substitui a fonte. Na dúvida, consulte
GET /v1/jobs/{id}.

