Webhooks
Os webhooks permitem que sua aplicação receba notificações em tempo real quando jobs assíncronos mudam de status. Em vez de fazer polling na API de Jobs, você pode configurar um endpoint de webhook para receber automaticamente atualizações quando jobs são criados, iniciados, concluídos ou falham.
Visão Geral
Os webhooks da TranscriMed são requisições HTTP POST enviadas para seu endpoint configurado sempre que eventos específicos ocorrem. Todas as requisições de webhook são assinadas com HMAC-SHA256 para segurança e incluem informações detalhadas sobre o evento no payload.
Recursos Principais
- Notificações em tempo real para eventos de jobs assíncronos
- Requisições assinadas com HMAC-SHA256 para segurança
- Tentativas automáticas com backoff exponencial
- Conteúdo completo do registro médico em webhooks de jobs concluídos
- Logs de entrega e monitoramento no Portal do Desenvolvedor
- Funcionalidade de endpoint de teste
Eventos de Webhook
Configure quais eventos você deseja receber:
| Evento | Descrição |
|---|---|
job.created | Disparado quando um job assíncrono é criado e enfileirado |
job.started | Disparado quando um job começa a ser processado |
job.completed | Disparado quando um job termina com sucesso |
job.failed | Disparado quando um job falha com erros |
job.cancelled | Disparado quando um job é cancelado |
Configuração
1. Configurar Endpoint de Webhook
No Portal do Desenvolvedor, configure seu webhook baseado no tipo de cliente:
Para Clientes de Teste (desenvolvimento/testes):
- URL do Webhook de Teste: URL do seu endpoint (HTTP localhost permitido, ex:
http://localhost:3000/webhooks/transcrimed) - Eventos: Selecione quais eventos receber
- Gerar Secret: Crie um secret de webhook de teste para verificação de assinatura
- Habilitar: Ative as notificações de webhook de teste
Para Clientes de Produção (integração ao vivo):
- URL do Webhook de Produção: Seu endpoint HTTPS (ex:
https://sua-api.com/webhooks/transcrimed) - Eventos: Selecione quais eventos receber
- Gerar Secret: Crie um secret de webhook de produção para verificação de assinatura
- Habilitar: Ative as notificações de webhook de produção
2. Implementar Endpoint de Webhook
Seu endpoint de webhook deve:
- Aceitar requisições HTTP POST
- Verificar assinaturas HMAC (recomendado)
- Responder com status HTTP 200 para processamento bem-sucedido
- Processar requisições de forma idempotente (o mesmo payload pode ser enviado múltiplas vezes)
Exemplo de Implementação (Node.js):
const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware para capturar body raw para verificação de assinatura
app.use('/webhooks/transcrimed', express.raw({ type: 'application/json' }));
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const receivedSignature = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
app.post('/webhooks/transcrimed', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const webhookSecret = process.env.TRANSCRIMED_WEBHOOK_SECRET;
// Verificar assinatura
if (!verifyWebhookSignature(req.body, signature, webhookSecret)) {
return res.status(401).send('Assinatura inválida');
}
const payload = JSON.parse(req.body);
// Processar evento do webhook
switch (payload.event) {
case 'test.ping':
// Webhook de teste do Portal do Desenvolvedor - apenas responder com 200
console.log('Webhook de teste recebido:', payload.message);
break;
case 'job.completed':
handleJobCompleted(payload);
break;
case 'job.failed':
handleJobFailed(payload);
break;
// Tratar outros eventos...
}
res.status(200).send('OK');
});
function handleJobCompleted(payload) {
const { job_id, external_reference_id } = payload;
const medicalRecord = payload.data.result?.medical_record;
if (medicalRecord) {
console.log(`Job ${job_id} concluído:`, {
recordId: medicalRecord.id,
title: medicalRecord.title,
externalRef: external_reference_id
});
// Processar o registro médico concluído
// O conteúdo HTML completo está disponível em medicalRecord.content
}
}
Payload do Webhook
Estrutura Comum do Payload
Todos os payloads de webhook seguem esta estrutura:
{
"event": "job.completed",
"job_id": "01234567-89ab-cdef-0123-456789abcdef",
"external_reference_id": "seu-id-rastreamento",
"timestamp": "2024-01-15T10:35:00Z",
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"type": "medical_record_generation",
"status": "completed",
"progress": 100,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z",
"completed_at": "2024-01-15T10:35:00Z",
"external_reference_id": "seu-id-rastreamento",
// Dados específicos do evento...
}
}
Payload de Job Concluído
Quando um job é concluído com sucesso, o payload inclui o registro médico completo:
{
"event": "job.completed",
"job_id": "01234567-89ab-cdef-0123-456789abcdef",
"external_reference_id": "visita-paciente-123",
"timestamp": "2024-01-15T10:35:00Z",
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"type": "medical_record_generation",
"status": "completed",
"progress": 100,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": "2024-01-15T10:35:00Z",
"external_reference_id": "visita-paciente-123",
"result": {
"medical_record": {
"id": "fedcba98-7654-3210-fedc-ba9876543210",
"user_id": "user-uuid",
"title": "Consulta de Cardiologia",
"content": "<html><body><h1>Registro Médico</h1>...</body></html>",
"template_id": "cardio-template-uuid",
"patient_id": "paciente-123",
"created_at": "2024-01-15T10:35:00Z",
"updated_at": "2024-01-15T10:35:00Z",
"metadata": {
"transcript": "Paciente relata dor no peito...",
"original_transcript": "Paciente relata dor no peito...",
"normalized_transcript": "Paciente relata dor no peito...",
"processing_duration": 45000,
"request_id": "req_abc123",
"external_reference_id": "visita-paciente-123"
}
},
"processing_info": {
"mode": "async",
"duration_ms": 45000,
"template_used": "cardio-template-uuid",
"job_id": "01234567-89ab-cdef-0123-456789abcdef"
}
}
}
}
Payload de Job com Falha
Quando um job falha, o payload inclui detalhes do erro:
{
"event": "job.failed",
"job_id": "01234567-89ab-cdef-0123-456789abcdef",
"external_reference_id": "visita-paciente-123",
"timestamp": "2024-01-15T10:35:00Z",
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"type": "medical_record_generation",
"status": "failed",
"progress": 50,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": "2024-01-15T10:35:00Z",
"external_reference_id": "visita-paciente-123",
"error": {
"error": "Processamento de áudio falhou: Formato de áudio inválido",
"timestamp": "2024-01-15T10:35:00Z",
"worker_id": "worker-abc123"
}
}
}
Segurança
Verificação de Assinatura HMAC
Todas as requisições de webhook incluem uma assinatura HMAC-SHA256 no header X-Webhook-Signature:
X-Webhook-Signature: sha256=a8b7c6d5e4f3g2h1...
Sempre verifique as assinaturas para garantir que as requisições são da TranscriMed:
import hmac
import hashlib
def verify_webhook_signature(payload_body, signature_header, webhook_secret):
"""Verificar assinatura do webhook"""
expected_signature = hmac.new(
webhook_secret.encode('utf-8'),
payload_body,
hashlib.sha256
).hexdigest()
received_signature = signature_header.replace('sha256=', '')
return hmac.compare_digest(expected_signature, received_signature)
Melhores Práticas
- Sempre verifique assinaturas antes de processar payloads
- Use endpoints HTTPS para URLs de webhook
- Implemente idempotência - o mesmo payload pode ser enviado múltiplas vezes
- Responda rapidamente - processe webhooks de forma assíncrona se necessário
- Retorne HTTP 200 para processamento bem-sucedido
- Registre eventos de webhook para depuração e monitoramento
- Rotacione secrets de webhook regularmente
Entrega e Tentativas
Mecanismo de Entrega
- Webhooks são entregues via HTTP POST para sua URL configurada
- Content-Type:
application/json - User-Agent:
TranscriMed-Webhook/1.0 - Timeout: 30 segundos por requisição
Lógica de Tentativas
Se seu endpoint de webhook não responder com HTTP 200:
- Tentativa imediata após 1 segundo
- Segunda tentativa após 2 segundos
- Terceira tentativa após 4 segundos
- Sem tentativas para erros 4xx do cliente (exceto 408, 429)
Headers
Cada requisição de webhook inclui estes headers:
Content-Type: application/json
X-Webhook-Signature: sha256=assinatura
X-Webhook-Event: job.completed
X-Webhook-Timestamp: 2024-01-15T10:35:00Z
User-Agent: TranscriMed-Webhook/1.0
Testes
Webhooks de Teste vs Produção
A TranscriMed mantém consistência entre testes de API e testes de webhook:
- Credenciais de Cliente de Teste: Use URLs de webhook de teste para desenvolvimento e testes
- Credenciais de Cliente de Produção: Use URLs de webhook de produção para integração ao vivo
Testando com o Portal do Desenvolvedor
O botão de teste de webhook usa testes do lado do servidor com assinaturas HMAC adequadas:
- Para Clientes de Teste: Testa sua URL de webhook de teste configurada com assinaturas válidas
- Para Clientes de Produção: Testa sua URL de webhook de produção configurada com assinaturas válidas
- Segurança: Todas as requisições de teste incluem assinaturas HMAC-SHA256 adequadas usando seu secret configurado
- Resultados Abrangentes: Retorna mensagens de erro detalhadas e orientação para solução de problemas
Testes HTTPS Locais
Para testes de webhook de produção, você precisa de endpoints HTTPS. Aqui estão várias abordagens para testes HTTPS locais:
Opção 1: ngrok (Recomendado)
ngrok cria túneis seguros para seu localhost, perfeito para testes de webhook:
# Instalar ngrok
npm install -g ngrok
# ou visite https://ngrok.com/download
# Iniciar seu servidor de webhook local
node test-webhook-server.js
# Em outro terminal, criar túnel HTTPS
ngrok http 3000
# Use a URL HTTPS (ex: https://abc123.ngrok.io) no Portal do Desenvolvedor
Benefícios:
- HTTPS real com certificados válidos
- URL pública acessível dos servidores TranscriMed
- Inspeção de requisições com interface web do ngrok
- Tier gratuito disponível
Opção 2: LocalTunnel
LocalTunnel fornece funcionalidade de tunelamento similar:
# Instalar localtunnel
npm install -g localtunnel
# Iniciar seu servidor de webhook local
node test-webhook-server.js
# Criar túnel
lt --port 3000 --subdomain nome-sua-app
# Use a URL HTTPS no Portal do Desenvolvedor
Opção 3: SSL Local com mkcert
mkcert cria certificados de desenvolvimento confiáveis localmente:
# Instalar mkcert
brew install mkcert # macOS
# ou visite https://github.com/FiloSottile/mkcert
# Criar CA local
mkcert -install
# Gerar certificados para localhost
mkcert localhost 127.0.0.1 ::1
# Use certificados em seu servidor (veja exemplo abaixo)
Exemplo de servidor HTTPS com mkcert:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('localhost-key.pem'),
cert: fs.readFileSync('localhost.pem')
};
https.createServer(options, (req, res) => {
// Seu código de handler de webhook
}).listen(3000, () => {
console.log('Servidor HTTPS rodando em https://localhost:3000');
});
Nota: Esta abordagem só funciona para testes na mesma máquina, pois os certificados não são publicamente acessíveis.
Opção 4: Cloudflare Tunnel
Cloudflare Tunnel fornece tunelamento seguro gratuito:
# Instalar cloudflared
brew install cloudflared # macOS
# ou visite https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/
# Iniciar túnel
cloudflared tunnel --url http://localhost:3000
# Use a URL HTTPS fornecida
Testando Assinaturas de Webhook Localmente
Servidor de Teste Aprimorado
Use o transcrimed-api-test-server.js aprimorado com verificação HMAC:
# Executar com verificação de assinatura
WEBHOOK_SECRET=seu_webhook_secret node transcrimed-api-test-server.js
# O servidor irá:
# Verificar assinaturas HMAC-SHA256
# Exibir resultados de verificação
# Registrar todos os detalhes do webhook
# Fornecer mensagens de erro úteis
Verificação Manual de Assinatura
Exemplo de código Node.js para verificar assinaturas de webhook:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
const receivedSignature = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
// Em seu handler de webhook:
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const isValid = verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET);
if (!isValid) {
return res.status(401).send('Assinatura inválida');
}
// Processar webhook...
res.json({success: true});
});
Exemplos de Verificação de Assinatura
Aqui estão exemplos completos para verificar assinaturas de webhook em diferentes linguagens de programação:
Node.js (Express)
const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware para capturar body raw para verificação de assinatura
app.use('/webhooks/transcrimed', express.raw({ type: 'application/json' }));
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const receivedSignature = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
app.post('/webhooks/transcrimed', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const webhookSecret = process.env.TRANSCRIMED_WEBHOOK_SECRET;
// Verificar assinatura
if (!verifyWebhookSignature(req.body, signature, webhookSecret)) {
return res.status(401).send('Assinatura inválida');
}
const payload = JSON.parse(req.body);
// Processar evento de webhook
console.log('Webhook recebido:', payload.event);
res.json({ success: true });
});
app.listen(3000, () => {
console.log('Servidor de webhook ouvindo na porta 3000');
});
Python (FastAPI)
import hmac
import hashlib
import json
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
app = FastAPI()
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
received_signature = signature.replace('sha256=', '')
return hmac.compare_digest(expected_signature, received_signature)
@app.post("/webhooks/transcrimed")
async def webhook_handler(request: Request):
signature = request.headers.get('x-webhook-signature')
webhook_secret = os.getenv('TRANSCRIMED_WEBHOOK_SECRET')
if not signature or not webhook_secret:
raise HTTPException(status_code=401, detail="Assinatura ou secret ausente")
body = await request.body()
# Verificar assinatura
if not verify_webhook_signature(body, signature, webhook_secret):
raise HTTPException(status_code=401, detail="Assinatura inválida")
payload = json.loads(body)
# Processar evento de webhook
print(f"Webhook recebido: {payload.get('event')}")
return JSONResponse({"success": True})
PHP (Slim Framework)
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
$app = AppFactory::create();
function verifyWebhookSignature($payload, $signature, $secret) {
$expectedSignature = hash_hmac('sha256', $payload, $secret);
$receivedSignature = str_replace('sha256=', '', $signature);
return hash_equals($expectedSignature, $receivedSignature);
}
$app->post('/webhooks/transcrimed', function (Request $request, Response $response) {
$signature = $request->getHeaderLine('x-webhook-signature');
$webhookSecret = $_ENV['TRANSCRIMED_WEBHOOK_SECRET'];
if (empty($signature) || empty($webhookSecret)) {
$response->getBody()->write('Assinatura ou secret ausente');
return $response->withStatus(401);
}
$body = $request->getBody()->getContents();
// Verificar assinatura
if (!verifyWebhookSignature($body, $signature, $webhookSecret)) {
$response->getBody()->write('Assinatura inválida');
return $response->withStatus(401);
}
$payload = json_decode($body, true);
// Processar evento de webhook
error_log('Webhook recebido: ' . $payload['event']);
$response->getBody()->write(json_encode(['success' => true]));
return $response->withHeader('Content-Type', 'application/json');
});
$app->run();
?>
Go (Gin Framework)
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"github.com/gin-gonic/gin"
)
func verifyWebhookSignature(payload []byte, signature string, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expectedSignature := hex.EncodeToString(mac.Sum(nil))
receivedSignature := strings.TrimPrefix(signature, "sha256=")
return hmac.Equal([]byte(expectedSignature), []byte(receivedSignature))
}
func webhookHandler(c *gin.Context) {
signature := c.GetHeader("x-webhook-signature")
webhookSecret := os.Getenv("TRANSCRIMED_WEBHOOK_SECRET")
if signature == "" || webhookSecret == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Assinatura ou secret ausente"})
return
}
body, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Falha ao ler body"})
return
}
// Verificar assinatura
if !verifyWebhookSignature(body, signature, webhookSecret) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Assinatura inválida"})
return
}
var payload map[string]interface{}
if err := json.Unmarshal(body, &payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "JSON inválido"})
return
}
// Processar evento de webhook
log.Printf("Webhook recebido: %v", payload["event"])
c.JSON(http.StatusOK, gin.H{"success": true})
}
func main() {
r := gin.Default()
r.POST("/webhooks/transcrimed", webhookHandler)
r.Run(":3000")
}
Testando Verificação de Assinatura
Use nosso servidor de teste aprimorado para validar sua implementação:
- Inicie seu servidor de webhook com verificação de assinatura
- Execute o servidor de teste com seu webhook secret:
WEBHOOK_SECRET=seu_secret node scripts/test-webhook-server.js - Configure webhook no Portal do Desenvolvedor com URL do seu servidor
- Teste webhook - o servidor de teste verificará a assinatura e mostrará resultados
Problemas Comuns de Assinatura
Problemas de Incompatibilidade de Assinatura:
- Usando webhook secret errado
- Modificando body da requisição antes da verificação
- Codificação incorreta (use bytes raw, não string)
- Algoritmo HMAC errado (use SHA-256)
Melhores Práticas:
- Verifique assinaturas ANTES de fazer parse do JSON
- Use body raw da requisição para cálculo de assinatura
- Armazene webhook secrets com segurança (variáveis de ambiente)
- Use funções de comparação timing-safe
- Registre falhas de verificação de assinatura para depuração
Isso permite testar webhooks localhost durante desenvolvimento ao usar credenciais de cliente de teste.
Para testar seu webhook:
- Vá para Webhooks
- Configure sua URL de webhook (pode ser localhost para clientes de teste)
- Clique em "Testar Webhook"
Guia de Solução de Problemas
Problemas Comuns de Webhook e Soluções
Falhas de Conexão
Problema: NETWORK_ERROR - Conexão recusada ou host inacessível
Soluções:
- Verificar status do servidor: Garanta que seu servidor de webhook está rodando
- Verificar porta: Confirme que a porta na URL do webhook corresponde ao seu servidor
- Verificar firewall: Garanta que nenhum firewall está bloqueando conexões
- Desenvolvimento local: Use
host.docker.internalem vez delocalhostpara ambientes containerizados
Exemplo de Correção:
# Em vez de: http://localhost:3000/webhook
# Use para Docker: http://host.docker.internal:3000/webhook
Erros de Resolução DNS
Problema: DNS_ERROR - Não é possível resolver hostname
Soluções:
- Verificar domínio: Verifique se o domínio existe e é publicamente acessível
- Testar DNS: Use
nslookupoudigpara verificar resolução DNS - Usar endereço IP: Tente usar endereço IP em vez de hostname para testes
Exemplo:
# Testar resolução DNS
nslookup seuwebhook.exemplo.com
dig seuwebhook.exemplo.com
Problemas de Certificado SSL/TLS
Problema: SSL_ERROR - Validação de certificado falhou
Soluções:
- Verificar certificado: Garanta que o certificado SSL é válido e não expirou
- Usar HTTPS: Webhooks de produção devem usar URLs HTTPS
- Cadeia de certificados: Verifique se a cadeia completa de certificados está configurada
- Endpoints de teste: Para desenvolvimento, use HTTP com credenciais de teste
Verificação Rápida:
# Testar certificado SSL
curl -I https://seuwebhook.exemplo.com/webhook
openssl s_client -connect seuwebhook.exemplo.com:443
Problemas de Timeout
Problema: TIMEOUT_ERROR - Timeout da requisição (>10 segundos)
Soluções:
- Otimizar handler: Garanta que o handler do webhook responde em 10 segundos
- Processamento assíncrono: Mova operações pesadas para jobs em background
- Resposta rápida: Retorne HTTP 200 imediatamente, processe webhook de forma assíncrona
- Verificar carga: Monitore carga do servidor e uso de recursos
Exemplo de Handler Rápido:
app.post('/webhook', (req, res) => {
// Responder imediatamente
res.json({ success: true });
// Processar de forma assíncrona
setImmediate(() => {
processWebhookAsync(req.body);
});
});
Problemas de Código de Status HTTP
Problema: HTTP_CLIENT_ERROR (4xx) ou HTTP_SERVER_ERROR (5xx)
Soluções por Código de Status:
| Status | Problema | Solução |
|---|---|---|
400 | Bad Request | Verificar parsing do body da requisição |
401 | Não Autorizado | Verificar lógica de validação de assinatura |
404 | Não Encontrado | Verificar caminho da URL do webhook |
405 | Método Não Permitido | Garantir que endpoint aceita requisições POST |
500 | Erro Interno | Verificar logs do servidor para erros |
502 | Bad Gateway | Verificar configuração de proxy/load balancer |
503 | Serviço Indisponível | Verificar capacidade e saúde do servidor |
Problemas de Verificação de Assinatura
Problema: Verificação de assinatura sempre falha
Passos de Debug:
-
Verificar webhook secret:
# Verificar se está usando o secret correto
echo "Seu webhook secret: $WEBHOOK_SECRET" -
Registrar assinaturas recebidas vs esperadas:
console.log('Assinatura recebida:', req.headers['x-webhook-signature']);
console.log('Assinatura esperada:', expectedSignature);
console.log('Tamanho do body:', req.body.length); -
Verificar uso de body raw:
// Errado - body foi parseado como JSON
const signature = generateSignature(JSON.stringify(req.body));
// Correto - usar buffer de body raw
const signature = generateSignature(req.body); -
Verificar codificação:
// Garantir codificação consistente
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8') // Especificar codificação
.digest('hex');
Problemas de CORS (Testes no Navegador)
Problema: Erros de CORS ao testar do navegador
Soluções:
-
Adicionar headers CORS:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, X-Webhook-Signature');
next();
}); -
Tratar requisições preflight:
app.options('/webhook', (req, res) => {
res.sendStatus(200);
});
Melhores Práticas de Teste
Desenvolvimento Local
- Use credenciais de teste: Sempre use credenciais de cliente de teste para desenvolvimento local
- Comece simples: Teste sem verificação de assinatura primeiro, depois adicione segurança
- Use servidor de teste: Aproveite nosso
transcrimed-api-test-server.jspara testes iniciais - Verifique logs: Monitore tanto logs da TranscriMed quanto logs do seu servidor
Testes de Produção
- Use HTTPS: Webhooks de produção requerem endpoints seguros
- Teste completamente: Use ambiente de staging antes de produção
- Monitore erros: Configure rastreamento e alertas de erros
- Implemente tentativas: Trate falhas temporárias graciosamente
Testes de Segurança
- Verifique assinaturas: Sempre valide assinaturas HMAC em produção
- Teste com secrets errados: Garanta que assinaturas inválidas são rejeitadas
- Limitação de taxa: Implemente limitação de taxa em endpoints de webhook
- Registre eventos de segurança: Rastreie tentativas de autenticação falhadas
Obtendo Ajuda
Se ainda estiver com problemas:
- Verifique logs de webhook no Portal do Desenvolvedor
- Teste com nosso servidor de teste:
node transcrimed-api-test-server.js - Revise mensagens de erro: O teste de webhook fornece informações detalhadas de erro
- Entre em contato com suporte: developers@transcrimed.com.br
- Verifique logs do seu servidor para verificar se o payload foi recebido
Payload de Teste
O webhook de teste envia este payload simples:
{
"event": "test.ping",
"test_mode": true,
"timestamp": "2024-01-15T10:35:00Z",
"message": "Webhook de teste do Portal do Desenvolvedor TranscriMed",
"client_type": "test"
}
Para Desenvolvimento Local:
- Use credenciais de cliente de teste do seu Portal do Desenvolvedor
- Configure URL de webhook de teste para
http://localhost:3000/webhook(ou seu servidor local) - Clique em "Testar Webhook" para verificar que seu servidor local recebe o payload
- Seu servidor de webhook deve responder com HTTP 200
Para Testes de Produção:
- Use credenciais de cliente de produção
- Configure URL de webhook de produção (deve ser HTTPS)
- Teste com servidores de staging/produção antes de ir ao vivo
Monitoramento
Logs de Entrega
O Portal do Desenvolvedor mostra logs de entrega para todas as tentativas de webhook:
- Status: Código de resposta HTTP ou erro
- Evento: Qual evento disparou o webhook
- Timestamp: Quando o webhook foi enviado
- Tentativas: Número de tentativas de entrega
- Resposta: Body da resposta do seu endpoint
Solução de Problemas
Problemas de Webhook de Teste
Como webhooks de teste são chamados diretamente do seu navegador:
| Problema | Solução |
|---|---|
| Conexão recusada/Erro de rede | Verifique se seu servidor de webhook está rodando e acessível do seu navegador |
| Erros de CORS | Configure seu servidor para aceitar requisições do domínio do Portal do Desenvolvedor |
| Timeout durante teste | Garanta que seu endpoint de webhook responde rapidamente (menos de 30 segundos) |
| 404 Não Encontrado | Verifique o caminho da URL do webhook e método HTTP (deve aceitar POST) |
Problemas de Webhook de Produção
Para webhooks de produção entregues pelos servidores TranscriMed:
| Problema | Solução |
|---|---|
| Verificação de assinatura falha | Verifique webhook secret e implementação HMAC |
| Timeouts | Garanta que seu endpoint responde em 30 segundos |
| Erros 4xx | Corrija URL do endpoint, autenticação ou tratamento de requisição |
| Webhooks ausentes | Verifique configuração de eventos e disponibilidade do endpoint |
| Erros SSL/TLS | Verifique se seu certificado HTTPS é válido e configurado corretamente |
Dicas de Desenvolvimento Local
- Use credenciais de cliente de teste para testes de webhook localhost
- Garanta que seu servidor local aceita requisições POST
- Verifique configurações de firewall se a conexão falhar
- Para problemas de CORS, configure seu servidor para aceitar requisições do navegador
- Teste com um servidor HTTP simples primeiro para verificar conectividade
Exemplos
Handler de Webhook Completo (Express.js)
const express = require('express');
const crypto = require('crypto');
const app = express();
// Parser de body raw para verificação de assinatura
app.use('/webhooks/transcrimed', express.raw({ type: 'application/json' }));
class WebhookHandler {
constructor(secret) {
this.secret = secret;
}
verifySignature(payload, signature) {
const expectedSignature = crypto
.createHmac('sha256', this.secret)
.update(payload)
.digest('hex');
const receivedSignature = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
async handleJobCompleted(payload) {
const { job_id, external_reference_id, data } = payload;
const medicalRecord = data.result?.medical_record;
if (!medicalRecord) {
console.error('Sem registro médico no payload do job concluído');
return;
}
// Salvar registro médico no seu banco de dados
await this.saveMedicalRecord({
id: medicalRecord.id,
title: medicalRecord.title,
content: medicalRecord.content,
patientId: medicalRecord.patient_id,
externalRef: external_reference_id,
metadata: medicalRecord.metadata
});
console.log(`Job ${job_id} concluído - registro médico ${medicalRecord.id} salvo`);
}
async handleJobFailed(payload) {
const { job_id, external_reference_id, data } = payload;
const error = data.error;
// Registrar falha e notificar sistemas relevantes
console.error(`Job ${job_id} falhou:`, error);
// Atualizar seu sistema para refletir a falha
await this.markJobAsFailed(external_reference_id, error);
}
async saveMedicalRecord(record) {
// Implemente sua lógica de salvamento no banco
console.log('Salvando registro médico:', record.title);
}
async markJobAsFailed(externalRef, error) {
// Implemente sua lógica de tratamento de erro
console.log('Marcando job como falhou:', externalRef, error);
}
}
const webhookHandler = new WebhookHandler(process.env.TRANSCRIMED_WEBHOOK_SECRET);
app.post('/webhooks/transcrimed', async (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!signature) {
return res.status(400).send('Assinatura ausente');
}
if (!webhookHandler.verifySignature(req.body, signature)) {
return res.status(401).send('Assinatura inválida');
}
try {
const payload = JSON.parse(req.body);
switch (payload.event) {
case 'test.ping':
// Webhook de teste do Portal do Desenvolvedor - apenas registrar e responder
console.log('Webhook de teste recebido do Portal do Desenvolvedor:', payload.message);
break;
case 'job.completed':
await webhookHandler.handleJobCompleted(payload);
break;
case 'job.failed':
await webhookHandler.handleJobFailed(payload);
break;
case 'job.started':
console.log(`Job ${payload.job_id} iniciou processamento`);
break;
case 'job.created':
console.log(`Job ${payload.job_id} criado e enfileirado`);
break;
default:
console.log(`Evento desconhecido: ${payload.event}`);
}
res.status(200).send('OK');
} catch (error) {
console.error('Erro ao processar webhook:', error);
res.status(500).send('Erro interno do servidor');
}
});
app.listen(3000, () => {
console.log('Servidor de webhook ouvindo na porta 3000');
});
Próximos Passos
- Configure seu endpoint de webhook com verificação de assinatura
- Configure webhooks no Portal do Desenvolvedor
- Teste sua integração usando o recurso de teste de webhook
- Monitore logs de entrega para garantir processamento confiável de webhook
- Implemente tratamento de erros para entregas de webhook falhadas
Para mais exemplos e SDKs, visite nossa página de Exemplos.