API de laudos
Criar, ler, assinar laudos programaticamente
A API de laudos permite enviar PDFs de laudos radiológicos a cada exame, ler o laudo vigente, excluí-lo e assiná-lo eletronicamente — tudo do seu sistema. A assinatura gera um signature_id com hash SHA-256 do PDF e uma URL pública de verificação (com QR opcional) para que qualquer um valide a autenticidade.
Enviar ou substituir um laudo
Envie o PDF como base64 (sem o prefixo data:application/pdf;base64,). Se já existe um laudo, substituímos. Esta operação dispara o webhook report.published.
/api/v1/exams/{id}/reportcurl -X POST "https://cbcthub.com/api/v1/exams/$EXAM_ID/report" \
-H "Authorization: Bearer $CBCTHUB_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"pdf_base64\": \"JVBERi0xLjQKJeLjz9MKMyA...\",
\"filename\": \"informe_juan_perez.pdf\"
}"
→ {
"ok": true,
"exam_id": "exm_abc123",
"report": {
"filename": "informe_juan_perez.pdf",
"size_bytes": 412300,
"created_at": "2026-06-23T14:35:01Z"
}
}Ler o laudo vigente
Retorna URL pré-assinada do PDF (válida 15 minutos) e, se já estiver assinado, os dados da assinatura: signature_id, hash SHA-256, data, URL pública de verificação.
/api/v1/exams/{id}/reportcurl "https://cbcthub.com/api/v1/exams/$EXAM_ID/report" \
-H "Authorization: Bearer $CBCTHUB_API_KEY"
→ {
"ok": true,
"exam_id": "exm_abc123",
"report": {
"filename": "informe_juan_perez.pdf",
"size_bytes": 412300,
"url": "https://r2.cloudflarestorage.com/...?signed",
"created_at": "2026-06-23T14:35:01Z",
"signature": {
"signature_id": "sig_9z8y7x6w5v",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"signed_at": "2026-06-23T14:36:10Z",
"verification_url": "https://cbcthub.com/v/sig_9z8y7x6w5v"
}
}
}Excluir o laudo
Remove o laudo vigente do exame. Idempotente: retorna 200 mesmo se não havia laudo. Qualquer assinatura prévia continua no audit log mas sua URL pública para de funcionar.
/api/v1/exams/{id}/reportcurl -X DELETE "https://cbcthub.com/api/v1/exams/$EXAM_ID/report" \
-H "Authorization: Bearer $CBCTHUB_API_KEY"
→ { "ok": true }Assinar eletronicamente
Calcula o hash SHA-256 do PDF, registra na tabela de assinaturas e gera URL pública de verificação. A assinatura dispara o webhook report.signed. A URL é do tipo https://cbcthub.com/v/{signature_id}.
/api/v1/exams/{id}/report/signcurl -X POST "https://cbcthub.com/api/v1/exams/$EXAM_ID/report/sign" \
-H "Authorization: Bearer $CBCTHUB_API_KEY"
→ {
"ok": true,
"signature_id": "sig_9z8y7x6w5v",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"signed_at": "2026-06-23T14:36:10Z",
"verification_url": "https://cbcthub.com/v/sig_9z8y7x6w5v"
}Validações
- O arquivo DEVE ser um PDF válido: validamos magic bytes (%PDF-). Se enviar outra coisa, retornamos 400 invalid_request.
- Tamanho máximo: 20 MB por laudo. Se precisar mais, comprima imagens ou divida o documento.
- POST /report é idempotente por conteúdo: enviar o mesmo PDF duas vezes não duplica nem dispara o webhook duas vezes.
- Para assinar, o exame precisa já ter um laudo. Caso contrário, recebe 400 invalid_request.
Exemplo end-to-end (Node.js)
Envia um PDF gerado por puppeteer, assina eletronicamente e obtém a URL de verificação pública:
import { readFile } from 'node:fs/promises';
const API = 'https://cbcthub.com/api/v1';
const KEY = process.env.CBCTHUB_API_KEY!;
const examId = 'exm_abc123';
async function uploadAndSignReport(pdfPath: string) {
// 1. Leer el PDF y codificar en base64
const pdf = await readFile(pdfPath);
const pdf_base64 = pdf.toString('base64');
// 2. Subir el informe (dispara webhook report.published)
const upRes = await fetch(`${API}/exams/${examId}/report`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
pdf_base64,
filename: 'informe.pdf',
}),
});
if (!upRes.ok) throw new Error(`upload failed: ${upRes.status}`);
// 3. Firmar electrónicamente (dispara webhook report.signed)
const signRes = await fetch(`${API}/exams/${examId}/report/sign`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${KEY}` },
});
const { signature_id, verification_url, sha256 } = await signRes.json();
console.log('Informe firmado.');
console.log('URL pública:', verification_url);
console.log('Hash SHA-256:', sha256);
// verification_url se puede convertir en QR client-side con cualquier
// librería (qrcode, qrcode-generator) y agregar al PDF si lo deseas.
return { signature_id, verification_url };
}
uploadAndSignReport('./informe_juan_perez.pdf').catch(console.error);Webhooks relacionados
Se você tem webhooks configurados, essas operações disparam eventos automaticamente:
report.published— ao enviar ou substituir o PDF.report.signed— ao assinar eletronicamente.
ℹ Verificação pública