Referencia de endpoints
Todos los endpoints REST de la API v1
Referencia completa de cada endpoint disponible en la API v1. URL base: https://cbcthub.com/api/v1
/api/v1/meDevuelve info de tu cuenta (plan, storage, exam count). Útil para verificar que la key funciona.
{
"user_id": "...",
"email": "soporte@miclinica.com",
"plan": "pro",
"scopes": ["exams:read", "exams:write"],
"storage": { "used_bytes": 41200000000, "limit_bytes": 375809638400, "used_percent": 11 },
"exams": { "count": 110, "limit": 999999 }
}/api/v1/examsCrea un examen y devuelve URLs presignadas para upload directo a R2.
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
| name | string | ✓ | Nombre interno del examen (máx 200 chars). |
| exam_type | string | — | cbct (default), radiografia, mesh, atm. Radiografia requiere Pro. |
| patient_name | string | — | Nombre del paciente. Aparece en el visor. |
| patient_id | string | — | RUT, CPF, historia clínica. |
| birth_date | string ISO | — | YYYY-MM-DD. |
| reason | string | — | Motivo / hallazgos solicitados. |
| expiration_days | number | — | Días hasta expirar. null = nunca. |
| upload_mode | string | — | "files" (default) o "zip". En modo zip se devuelve 1 sola URL y luego se llama /process-zip. |
| files | array | — | [{ name, size }]. Requerido si upload_mode="files". Mínimo 1, máximo 5000. |
| zip_size_bytes | number | — | Requerido si upload_mode="zip". Máximo 1 GB (1073741824). |
/api/v1/exams?limit=20&offset=0Lista exámenes de tu cuenta, paginado. Default limit=20, máximo 100.
/api/v1/exams/{id}Detalle de un examen. Incluye share_url, viewer_url, share_password y public_page_pin.
/api/v1/exams/{id}/confirmMarca el examen como ready después de subir los archivos (modo files). Idempotente.
/api/v1/exams/{id}/process-zipPara exams creados con upload_mode="zip": descomprime server-side (streaming) el ZIP que subiste a staging, sube cada DICOM a R2 y marca el examen como ready. Idempotente. Límites: 1 GB de ZIP, 5000 archivos, 5 GB expandidos, 1,6 TB por archivo individual, 300 s de timeout.
/api/v1/exams/{id}Elimina el examen. Los archivos en R2 se borran en background. Idempotente: si ya estaba eliminado devuelve 200 con already_deleted=true. Scope: exams:write.
curl -X DELETE "https://cbcthub.com/api/v1/exams/EXAM_ID" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "deleted": true, "exam_id": "EXAM_ID" }/api/v1/exams/{id}Actualiza parcialmente el examen. Solo se modifican los campos enviados. Campos permitidos: name, patient_name, patient_id, birth_date, reason, expiration_days, radiograph_subtype (solo si exam_type=radiografia). No se puede cambiar exam_type, share_token, storage ni contadores.
curl -X PATCH "https://cbcthub.com/api/v1/exams/EXAM_ID" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"patient_name":"Juan Pérez","expiration_days":90}'/api/v1/exams/{id}/shareEnvía el examen por email al destinatario. Body: { to, cc?, message?, locale? }. En sandbox no manda email real (sent=false, reason=test_mode_no_send). Para radiografías adjunta las imágenes; para CBCT, el correo lleva el link al visor.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/share" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"to":"paciente@example.com","locale":"es"}'/api/v1/exams/{id}/extrasInicia una toma adicional para CBCT/ATM (multi-toma). Devuelve URLs presignadas para cada archivo. Body: { label, files: [{name, size}] }. Máximo 2 extras por examen (3 tomas totales). No aplica a radiografías. Scope: exams:write.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/extras" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Segunda toma post-ortodoncia",
"files": [{ "name": "IM0001.dcm", "size": 524288 }]
}'
# → { "index": 1, "upload_urls": [...], "confirm_url": ".../extras/1/confirm" }/api/v1/exams/{id}/extras/{index}/confirmConfirma que la toma extra terminó de subirse. Verifica los archivos en R2 y actualiza extra_series + storage. Body opcional: { label }. Idempotente. Scope: exams:write.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/extras/1/confirm" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"label":"Segunda toma post-ortodoncia"}'/api/v1/exams/{id}/access-pinDevuelve el PIN actual + info de la página pública del centro. Útil para mostrar el PIN al paciente sin tener que consultar el dashboard. Scope: exams:read.
curl "https://cbcthub.com/api/v1/exams/EXAM_ID/access-pin" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "pin": "482931", "clinic_slug": "miclinica", "public_url": "https://cbcthub.com/centro/miclinica" }/api/v1/exams/{id}/access-pinRota el PIN: genera uno nuevo y deja el viejo inservible al instante. Body vacío. Úsalo si un paciente filtró el PIN o tras un incidente. Scope: exams:write.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/access-pin" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "pin": "739184", "rotated_at": "2026-06-27T18:20:11Z" }/api/v1/exams/{id}/embedDevuelve el estado del embed (enabled, token, snippet, view_count). Scope: exams:read.
curl "https://cbcthub.com/api/v1/exams/EXAM_ID/embed" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/exams/{id}/embedHabilita el embed (genera token si no existe). Devuelve embed_url e iframe_snippet listo para pegar. Body opcional: { title, show_year, show_study_type }.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/embed" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Caso 12345","show_year":true}'/api/v1/exams/{id}/embedRevoca el embed: borra el token y deja el link existente sin servicio. Para reactivar, llama POST de nuevo (devuelve un token nuevo). Scope: exams:write.
curl -X DELETE "https://cbcthub.com/api/v1/exams/EXAM_ID/embed" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/exams/{id}/thumbnailDescarga el thumbnail del examen (JPEG/PNG) en binario. Si aún no se generó, devuelve 404 con thumbnail_not_ready. Ideal para mostrar miniaturas en tu UI. Scope: exams:read.
curl -o thumb.jpg "https://cbcthub.com/api/v1/exams/EXAM_ID/thumbnail" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/exams/{id}/patient-examsLista los otros exámenes del mismo paciente (match por patient_id; fallback a nombre normalizado). Útil para mostrar historial o habilitar comparadores. Scope: exams:read.
curl "https://cbcthub.com/api/v1/exams/EXAM_ID/patient-exams" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "exams": [{ "id": "...", "name": "...", "exam_type": "cbct", "created_at": "..." }] }Consentimiento informado
Flujo legal completo: inicialización por país/idioma, firma en consulta (paciente presente con tablet) o por email (link al teléfono del paciente), descarga del PDF firmado con SHA-256 y QR de verificación pública.
/api/v1/exams/{id}/consentDevuelve el estado actual del consentimiento (status, signing_method, signed_at, hash). Si no existe aún, devuelve { consent: null }. Scope: exams:read.
curl "https://cbcthub.com/api/v1/exams/EXAM_ID/consent" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/exams/{id}/consentInicializa (o re-inicializa si está pending) el consentimiento. Body opcional: { country_code, locale }. Si ya está firmado devuelve 409. Scope: exams:write.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/consent" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"country_code":"CL","locale":"es"}'/api/v1/exams/{id}/consent/sign-inpersonFirma en consulta. Body: { signer_name, signer_rut, patient_birth_date?, signature_image (PNG base64), accepted: true }. El servidor genera el PDF firmado y calcula SHA-256. Scope: exams:write.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/consent/sign-inperson" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{
"signer_name": "Juan Perez",
"signer_rut": "12345678-9",
"signature_image": "iVBORw0KGgoAAAANSUhEUgAA...",
"accepted": true
}'/api/v1/exams/{id}/consent/emailEnvía el link de firma al paciente. Body: { to, patient_name? }. En sandbox no envía email real. Devuelve sign_url para que muestres también un QR. Scope: exams:write.
curl -X POST "https://cbcthub.com/api/v1/exams/EXAM_ID/consent/email" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"to":"paciente@example.com","patient_name":"Juan Perez"}'/api/v1/exams/{id}/consent/pdf?type=blank|signedDescarga el PDF binario del consentimiento. type=blank devuelve el documento sin firmar (útil para imprimir). type=signed requiere que el consentimiento esté firmado. Scope: exams:read.
curl -o consent.pdf \
"https://cbcthub.com/api/v1/exams/EXAM_ID/consent/pdf?type=signed" \
-H "Authorization: Bearer $CBCTHUB_KEY"Informes radiológicos
Endpoints para subir, leer, eliminar y firmar electrónicamente informes en PDF. Detalle completo en la sección API de informes.
/api/v1/exams/{id}/reportSube o reemplaza el PDF del informe. Body: { pdf_base64, filename? }.
/api/v1/exams/{id}/reportDevuelve URL presignada del PDF + datos de firma si está firmado.
/api/v1/exams/{id}/reportElimina el informe. Idempotente: si no existe devuelve 200 igual.
/api/v1/exams/{id}/report/signFirma electrónicamente el informe. Devuelve signature_id y URL pública de verificación.
Webhooks
Endpoints para gestionar subscriptions de webhooks. Detalle completo (eventos, firma HMAC, retries) en la sección Webhooks.
/api/v1/webhooksCrea una subscription. Devuelve el secret UNA SOLA VEZ.
/api/v1/webhooksLista todas las subscriptions de la cuenta.
/api/v1/webhooks/{id}Detalle de una subscription.
/api/v1/webhooks/{id}Actualiza events, description o enabled.
/api/v1/webhooks/{id}Elimina la subscription. La key revocada no se reactiva.
/api/v1/webhooks/{id}/testEnvía un ping de prueba al endpoint configurado.
/api/v1/webhooks/{id}/deliveriesAudit log de las últimas 50 entregas (status, response, retry count).
Derivadores (Referrers)
CRUD del directorio de dentistas derivadores. Scopes nuevos: referrers:read y referrers:write. Sandbox: las lecturas devuelven lista vacía y las escrituras devuelven un mock con id "rfr_test_*" sin persistir, así puedes probar la integración sin ensuciar la base productiva.
/api/v1/referrersLista todos los derivadores de la cuenta, ordenados alfabéticamente por nombre. Scope: referrers:read.
curl "https://cbcthub.com/api/v1/referrers" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "referrers": [{ "id": "rfr_...", "name": "Dra. Maria Lopez", "email": "...", "specialty": "..." }] }/api/v1/referrersCrea un derivador. Body: { name (requerido), email?, phone?, specialty? }.
curl -X POST "https://cbcthub.com/api/v1/referrers" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Dra. Maria Lopez","email":"maria@example.com","specialty":"Endodoncia"}'/api/v1/referrers/{id}Detalle de un derivador. Scope: referrers:read.
/api/v1/referrers/{id}Actualiza parcialmente. Solo los campos enviados se modifican. Scope: referrers:write.
curl -X PATCH "https://cbcthub.com/api/v1/referrers/rfr_xyz" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"phone":"+56 9 1234 5678"}'/api/v1/referrers/{id}Elimina el derivador. Idempotente: si ya no existe devuelve already_deleted=true. Scope: referrers:write.
curl -X DELETE "https://cbcthub.com/api/v1/referrers/rfr_xyz" \
-H "Authorization: Bearer $CBCTHUB_KEY"Plantillas de informe (Templates)
CRUD de plantillas de placas para informes radiológicos. Las plantillas con is_default=true son del sistema y son visibles para todos los usuarios; no se pueden editar ni eliminar. Scopes nuevos: templates:read y templates:write. Sandbox: GET muestra solo las defaults y los writes son no-op (devuelven mock con id "tpl_test_*").
/api/v1/templatesLista las plantillas visibles (defaults system + propias del usuario). Scope: templates:read.
curl "https://cbcthub.com/api/v1/templates" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/templatesCrea una plantilla del usuario. Body: { name, description?, pages: PageDef[] }. Cada página: { name?, slots: [{ x, y, w, h, label? }] } con coordenadas 0..1. Scope: templates:write.
curl -X POST "https://cbcthub.com/api/v1/templates" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Periapical 14 placas",
"pages": [{
"name": "Pagina 1",
"slots": [
{ "x": 0.05, "y": 0.05, "w": 0.2, "h": 0.3, "label": "11" },
{ "x": 0.30, "y": 0.05, "w": 0.2, "h": 0.3, "label": "12" }
]
}]
}'/api/v1/templates/{id}Detalle de una plantilla (system o propia). Scope: templates:read.
curl "https://cbcthub.com/api/v1/templates/tpl_xyz" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/templates/{id}Actualiza una plantilla propia. Las defaults system devuelven 400. Scope: templates:write.
curl -X PATCH "https://cbcthub.com/api/v1/templates/tpl_xyz" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Periapical 14 placas v2"}'/api/v1/templates/{id}Elimina una plantilla propia. Idempotente. Las defaults system devuelven 400. Scope: templates:write.
curl -X DELETE "https://cbcthub.com/api/v1/templates/tpl_xyz" \
-H "Authorization: Bearer $CBCTHUB_KEY"Catálogo de implantes
Catálogo global de modelos de implantes (brand, product_line, longitud y diámetro). Read-only. Es un recurso de referencia, no por-usuario: usa el scope exams:read. Sandbox: el catálogo es idéntico que en live.
/api/v1/implantsSin parámetros devuelve la lista de marcas con count(*). Filtros: ?brand=Neodent (modelos de esa marca), ?search=helix (texto libre en brand+product_line), ?length=11.5&diameter=4 (búsqueda inversa por dimensiones; devuelve también near_matches a ±0.5 mm). Soporta ?limit= y ?offset=.
curl "https://cbcthub.com/api/v1/implants?brand=Neodent&limit=20" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# Busqueda por dimension
curl "https://cbcthub.com/api/v1/implants?length=11.5&diameter=4" \
-H "Authorization: Bearer $CBCTHUB_KEY"Tickets de soporte
Crea y lee tickets de soporte programáticamente. Scopes nuevos: support:read y support:write. Al crear un ticket en live se envía email al equipo de soporte y un correo de confirmación al cliente. Sandbox: NO persiste, NO envía emails, devuelve un mock con id "tkt_test_*" y test_mode=true.
/api/v1/support/ticketsLista los 50 tickets más recientes de la cuenta (configurable con ?limit=). Scope: support:read.
curl "https://cbcthub.com/api/v1/support/tickets?limit=20" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/support/ticketsCrea un ticket. Body: { subject, message, category?, exam_id? }. Categorías válidas: general, bug, billing, feature_request, account, viewer.
curl -X POST "https://cbcthub.com/api/v1/support/tickets" \
-H "Authorization: Bearer $CBCTHUB_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject":"Mi examen no se subio",
"message":"Subi un ZIP de 800 MB y aparece pending desde hace 2 horas.",
"category":"bug",
"exam_id":"b1f7c9c6-8c10-4d27-8d80-93e1f1a5d4af"
}'/api/v1/support/tickets/{id}Detalle del ticket + lista de respuestas del admin (replies en orden cronológico). Scope: support:read.
curl "https://cbcthub.com/api/v1/support/tickets/tkt_xyz" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "ticket": {...}, "replies": [{ "body": "...", "created_at": "..." }] }Activity y notificaciones
Lee el log de actividad de la cuenta y las notificaciones generadas (respuestas de soporte, eventos del sistema). Scopes nuevos: account:read y account:write. Sandbox: devuelve listas vacías.
/api/v1/activityLog de actividad paginado (descendente). Query: ?limit=50 (1-100), ?before=ISO_DATE (cursor — usa el next_cursor de la respuesta), ?action=exam.created (filtro opcional).
curl "https://cbcthub.com/api/v1/activity?limit=20&action=exam.created" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/notificationsLista las notificaciones genéricas + unread_count. Query: ?unread_only=true, ?limit=30. NO incluye aperturas de exámenes (esos eventos viajan por webhooks). Scope: account:read.
curl "https://cbcthub.com/api/v1/notifications?unread_only=true&limit=30" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "notifications": [...], "unread_count": 4 }/api/v1/notifications/{id}/mark-readMarca una notificación como leída. Idempotente. Scope: account:write.
curl -X POST "https://cbcthub.com/api/v1/notifications/ntf_xyz/mark-read" \
-H "Authorization: Bearer $CBCTHUB_KEY"/api/v1/notifications/mark-all-readMarca TODAS las no leídas como leídas y devuelve marked_count. Scope: account:write.
curl -X POST "https://cbcthub.com/api/v1/notifications/mark-all-read" \
-H "Authorization: Bearer $CBCTHUB_KEY"
# → { "marked_count": 4 }Prueba estos endpoints en vivo
GET /api/v1/exams
/api/v1/examsScope: exams:readPruébala ahoraLista exámenes de tu cuenta, paginados (newest first).
Pega aquí una API key (live o test) generada desde Ajustes → API. Crear o gestionar keys →
Parámetros de query
Request
GET https://cbcthub.com/api/v1/exams?limit=5&offset=0Tu API key se guarda solo en este navegador durante la sesión. No se envía a CBCTHub fuera de la request, ni se persiste al cerrar la pestaña.
POST /api/v1/exams
/api/v1/examsScope: exams:writePruébala ahoraCrea un examen y obtén URLs presignadas para upload directo a R2.
Pega aquí una API key (live o test) generada desde Ajustes → API. Crear o gestionar keys →
Request
POST https://cbcthub.com/api/v1/examsTu API key se guarda solo en este navegador durante la sesión. No se envía a CBCTHub fuera de la request, ni se persiste al cerrar la pestaña.
GET /api/v1/exams/{id}
/api/v1/exams/{id}Scope: exams:readPruébala ahoraDetalle de un examen. Necesitas un id de tu cuenta (puedes obtenerlo del playground de la lista de arriba).
Pega aquí una API key (live o test) generada desde Ajustes → API. Crear o gestionar keys →
Parámetros de path
Request
GET https://cbcthub.com/api/v1/exams/{id}Tu API key se guarda solo en este navegador durante la sesión. No se envía a CBCTHub fuera de la request, ni se persiste al cerrar la pestaña.
POST /api/v1/webhooks
/api/v1/webhooksScope: webhooks:writePruébala ahoraCrea una subscription de webhook. La response devuelve el secret UNA SOLA VEZ.
Pega aquí una API key (live o test) generada desde Ajustes → API. Crear o gestionar keys →
Request
POST https://cbcthub.com/api/v1/webhooksTu API key se guarda solo en este navegador durante la sesión. No se envía a CBCTHub fuera de la request, ni se persiste al cerrar la pestaña.
POST /api/v1/webhooks/{id}/test
/api/v1/webhooks/{id}/testScope: webhooks:writePruébala ahoraEnvía un ping de prueba (test.ping) al endpoint configurado.
Pega aquí una API key (live o test) generada desde Ajustes → API. Crear o gestionar keys →
Parámetros de path
Request
POST https://cbcthub.com/api/v1/webhooks/{id}/testTu API key se guarda solo en este navegador durante la sesión. No se envía a CBCTHub fuera de la request, ni se persiste al cerrar la pestaña.