Pular para o conteúdo principal
Docs vAtualAPI
Versão: Atual

Webhooks

Webhooks permitem integrar a SauBit com prontuários (HIS/EMR) e automações internas do hospital.

Quando configurado, a SauBit envia eventos via POST para um endpoint do hospital (ex: https://hospital.exemplo.com/webhooks/events).

Como configurar

O cadastro do webhook é feito no Admin (organização):

  • URL do destino
  • Eventos desejados (ou todos)
  • Secret (gerado e exibido uma única vez)

Headers enviados

  • X-SauBit-Event: tipo do evento
  • X-SauBit-Timestamp: epoch seconds
  • X-SauBit-Signature: assinatura HMAC

Assinatura (HMAC)

O corpo é assinado com:

HMAC_SHA256(secret, "{timestamp}.{raw_body}")

Formato do header:

X-SauBit-Signature: sha256=<hex>

Eventos

analysis.completed

Disparado quando uma análise termina (check ou recheck).

Payload:

{
"event": "analysis.completed",
"data": {
"analysis_id": "uuid",
"endpoint": "/interactions/check",
"cached": false,
"recheck_of": "uuid-opcional"
}
}

chat.message

Disparado quando o hospital envia uma mensagem no chat.

Payload:

{
"event": "chat.message",
"data": {
"session_id": "abc123",
"analysis_id": "uuid-opcional"
}
}

Exemplo de verificação (Node.js)

import crypto from "crypto";

// Aceita o secret atual e, durante a rotação, o anterior (X-SauBit-Signature-Previous).
export function verifySauBitWebhook(req, secrets) {
const timestamp = req.headers["x-saubit-timestamp"];
const rawBody = req.rawBody; // corpo bruto (bytes/string)
const msg = `${timestamp}.${rawBody}`;
const candidates = [
req.headers["x-saubit-signature"],
req.headers["x-saubit-signature-previous"],
].filter(Boolean);

for (const secret of [].concat(secrets)) {
const expected = `sha256=${crypto.createHmac("sha256", secret).update(msg).digest("hex")}`;
const expectedBuf = Buffer.from(expected);
for (const sig of candidates) {
const sigBuf = Buffer.from(String(sig));
// timingSafeEqual lança erro se os tamanhos diferem — verifique antes.
if (sigBuf.length === expectedBuf.length && crypto.timingSafeEqual(sigBuf, expectedBuf)) {
return true;
}
}
}
return false;
}

Exemplo de verificação (Python)

import hmac
import hashlib

def verify_saubit(signature: str, timestamp: str, raw_body: bytes, secret: str) -> bool:
msg = timestamp.encode() + b"." + raw_body
digest = hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
expected = f"sha256={digest}"
return hmac.compare_digest(signature, expected)

Envelope do evento

Cada entrega traz um envelope estável:

{
"event_id": "evt_uuid",
"event": "analysis.completed",
"schema_version": "1.0",
"created_at": "2026-06-30T12:00:00Z",
"delivery_id": "dlv_uuid",
"attempt": 1,
"data": { "...": "..." }
}
  • Entrega at-least-once: o mesmo event_id pode chegar duplicado — deduplique por event_id. delivery_id/attempt identificam a tentativa.
  • Eventos podem chegar fora de ordem — não assuma ordenação; use created_at.

Entrega, retentativa e replay

Cada disparo é entregue com até 3 tentativas (retentativa em erro de rede ou resposta não-2xx, com backoff). Toda entrega — sucesso ou falha — é registrada (evento, URL, status HTTP, nº de tentativas, último erro). Responda 2xx para confirmar o recebimento.

O reenvio manual de uma entrega passada (replay), a listagem do log de entregas e o envio de teste (webhook.test) são operações administrativas, no Swagger Admin (/docs/admin).

Rotação de secret (dois segredos válidos)

Ao rotacionar o secret (admin), o anterior continua válido durante a janela: cada disparo inclui X-SauBit-Signature (atual) e X-SauBit-Signature-Previous (anterior). Valide contra qualquer um dos dois — o exemplo acima já faz isso — para trocar sem perder entregas.