API de informes
Crear, leer, firmar informes programáticamente
La API de informes te permite subir PDFs de informes radiológicos a cada examen, leer el informe vigente, eliminarlo y firmarlo electrónicamente — todo desde tu sistema. La firma genera un signature_id con hash SHA-256 del PDF y una URL pública de verificación (con QR opcional) para que cualquiera pueda validar la autenticidad del documento.
Subir o reemplazar un informe
Envía el PDF como base64 (sin prefijo data:application/pdf;base64,). Si ya hay un informe en el examen, lo reemplazamos. Esta operación dispara el 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"
}
}Leer el informe vigente
Devuelve una URL presignada del PDF (válida 15 minutos) y, si ya está firmado, los datos de la firma: signature_id, hash SHA-256, fecha, URL pública de verificación.
/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"
}
}
}Eliminar el informe
Elimina el informe vigente del examen. Es idempotente: si no había informe devolvemos 200 igual. La firma previa (si existía) queda registrada en el audit log pero su URL pública deja 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 }Firmar electrónicamente
Calcula el hash SHA-256 del PDF, lo registra en la tabla de firmas y genera una URL pública de verificación. La firma dispara el webhook report.signed. La URL de verificación es del 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"
}Validaciones
- El archivo DEBE ser un PDF válido: validamos magic bytes (%PDF-). Si subes otra cosa, devolvemos 400 invalid_request.
- Tamaño máximo: 20 MB por informe. Si necesitas más, comprime imágenes o divide el documento.
- POST /report es idempotente por contenido: subir el mismo PDF dos veces no genera duplicados ni dispara el webhook dos veces.
- Para firmar, el examen tiene que tener un informe subido. Si no hay informe, recibes 400 invalid_request.
Ejemplo end-to-end (Node.js)
Sube un PDF generado por puppeteer, firma electrónicamente y obtiene la URL de verificación 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
Si tienes webhooks configurados, estas operaciones disparan eventos automáticamente:
report.published— al subir o reemplazar el PDF.report.signed— al firmar electrónicamente.
ℹ Verificación pública