Webhooks vs polling: notificações de exames em tempo real para fluxos de imagem odontológica
Toda integração de imagem odontológica acaba enfrentando a mesma pergunta arquitetural: como o app do dentista solicitante descobre que uma tomografia CBCT ficou pronta? Se a sua resposta é "fazemos polling à API a cada 30 segundos", você está introduzindo latência, gastando invocações serverless e drenando bateria de celular. Existe um caminho padrão melhor — webhooks event-driven com verificação HMAC e retries idempotentes — e este post defende a troca.
Os dois padrões em um parágrafo
Polling significa que seu código pergunta "já ficou pronto?" em um timer. Webhooks significa que o CBCTHub chama seu endpoint no instante em que o evento acontece. Polling é pull, webhooks é push. Ambos funcionam. Têm perfis muito diferentes de custo, latência e operação.
Comparação lado a lado
| Dimensão | Polling (a cada 30 s) | Webhooks |
|---|---|---|
| Latência mediana | 15 s (metade do intervalo) | < 1 s |
| Latência pior caso | 30 s + orçamento de retries | segundos, com retries por cima |
| Invocações cloud por 100 exames (upload de 10 min) | ~2.000 | ~100 |
| Impacto na bateria do celular | Alto — acorda o rádio a cada ciclo | Nenhum — o servidor empurra |
| Atrás de firewall / em laptop | Funciona sem problema | Precisa de URL pública |
| Depurabilidade | Trivial — você repete qualquer request | Precisa de logs de entrega + replay |
| Backpressure | Natural — você controla a frequência | O provedor precisa implementar filas |
Para fluxos típicos entre PMS e CBCTHub em que você quer que o app do dentista atualize no segundo exato em que o exame fica pronto, webhooks ganham em todas as dimensões que importam ao usuário final.
Quando polling ainda faz sentido
Webhooks não são uma religião. Use polling quando:
- Você está depurando localmente e não quer montar um túnel ngrok ou Cloudflare até seu laptop.
- Seu sistema não consegue expor URL pública — por exemplo, um PMS legado rodando na LAN da clínica sem IP público nem proxy reverso.
- Você processa resultados em batch — um cron noturno que importa os exames do dia funciona bem com polling.
- O evento é raro e você já faz polling em um gatilho de UX (por exemplo, quando o usuário abre a lista de exames).
Todo o resto — notificações em tempo real para o dentista, relays de push mobile, telas de status na sala de espera — pertence a webhooks.
Como o CBCTHub assina os webhooks
Cada entrega de webhook inclui dois headers:
X-CBCTHub-Signature: HMAC-SHA256 do corpo bruto da requisição, assinado com o segredo que você viu uma única vez ao criar a assinatura.X-CBCTHub-Delivery: um ID único desta tentativa de entrega. Use como sua chave de idempotência.
Verifique a assinatura antes de qualquer outra coisa. Se falhar, responda 401 e registre a tentativa — ou o segredo está mal configurado ou alguém está procurando vulnerabilidade.
Verificação em Node.js / Express
import crypto from 'crypto';
import express from 'express';
const app = express();
app.post(
'/cbcthub/webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.header('X-CBCTHub-Signature') ?? '';
const expected = crypto
.createHmac('sha256', process.env.CBCTHUB_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (
sig.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
) {
return res.status(401).send('assinatura inválida');
}
const deliveryId = req.header('X-CBCTHub-Delivery');
const event = JSON.parse(req.body.toString());
// Idempotência — devolva 200 se já processou esta entrega
if (await deliveries.has(deliveryId)) return res.status(200).send('duplicada');
await deliveries.add(deliveryId);
await handleEvent(event); // sua regra de negócio
return res.status(200).send('ok');
}
);
Verificação em Python / FastAPI
import hmac, hashlib, os
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
SECRET = os.environ['CBCTHUB_WEBHOOK_SECRET'].encode()
@app.post('/cbcthub/webhook')
async def webhook(req: Request):
body = await req.body()
sig = req.headers.get('x-cbcthub-signature', '')
expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, expected):
raise HTTPException(401, 'assinatura invalida')
delivery_id = req.headers.get('x-cbcthub-delivery')
event = await req.json()
if await deliveries.contains(delivery_id):
return {'status': 'duplicada'}
await deliveries.add(delivery_id)
await handle_event(event)
return {'status': 'ok'}
Idempotência: a regra que você não pode pular
O CBCTHub repete as entregas que não recebem 2xx, com backoff exponencial por até 24 horas. Isso significa que seu endpoint receberá o mesmo evento mais de uma vez em algum momento, seja por um 5xx vazado ou por timeout. Use X-CBCTHub-Delivery como chave de deduplicação no Redis, numa tabela do banco ou até num cache LRU, e responda 200 na segunda tentativa sem reexecutar efeitos colaterais.
Test mode: validar sem tocar em produção
Gere uma API key cbct_test_, registre um webhook contra ela e dispare um evento de teste no dashboard. Eventos disparados por uma key de teste trazem "livemode": false no payload, então seu código pode roteá-los a um banco de staging mantendo o mesmo handler. É a forma mais segura de subir uma integração de webhooks em produção sem risco de uma entrega real chegar na caixa errada.
Padrão: webhooks → fila durável → workers
O padrão mais robusto em produção mantém pequeno o handler de webhook: verifica assinatura, enfileira, responde 200. Um pool separado de workers consome a fila e faz o trabalho lento. Isso isola o ack do webhook da sua regra de negócio, então uma travada do banco nunca faz o CBCTHub recuar.
Stacks concretas:
- Vercel: rota do webhook → Inngest ou QStash → funções em background.
- AWS: API Gateway → SQS → consumers Lambda.
- Self-hosted: Express → stream do Redis → workers BullMQ.
- Cloudflare: Worker → Queue → Worker consumer.
Quando escolher o quê
Por padrão, webhooks para qualquer evento em tempo real voltado ao usuário. Deixe polling para importações noturnas, ferramentas de debug e sistemas legados on-prem que não aceitam tráfego entrante. O híbrido também vale — várias integrações em produção usam os dois, com webhooks como caminho principal e um poll diário de reconciliação como rede de segurança.
Conclusão
Polling é a resposta fácil no começo e a errada no longo prazo. Webhooks com assinatura HMAC, handlers idempotentes e uma fila pequena custam menos, escalam melhor e dão ao usuário final uma experiência em tempo real. O sistema de entrega de webhooks do CBCTHub foi construído para isso — retries automáticos, UI de replay, separação de livemode — então só falta você construir o receiver.
Prueba CBCTHub gratis
Sube, visualiza y comparte examenes DICOM en la nube. Sin instalar nada.
Crear cuenta gratisArticulos relacionados
Como integrar seu software de gestão odontológica com o CBCTHub: tutorial completo da API
Guia passo a passo para integrar um PMS odontológico com a API REST do CBCTHub. Envie exames, embuta o visualizador 3D e automatize a entrega em Node, Python e PHP.
APIs de imagem odontológica compatíveis com HIPAA: lista de verificação de segurança para integrações de saúde nos EUA
Checklist de 10 pontos para garantir compliance HIPAA em qualquer integração de API de imagem odontológica nos EUA. Criptografia, BAAs, audit logs, notificação de brechas.
Protecao de dados de pacientes nos EUA: guia HIPAA e HITECH para centros de radiologia dentaria
Como cumprir HIPAA (Public Law 104-191), Privacy Rule, Security Rule e HITECH Act em uma clinica de imagem dentaria. PHI, BAA, multas OCR ate USD 1.5M por ano.