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

GET/api/v1/me

Devuelve info de tu cuenta (plan, storage, exam count). Útil para verificar que la key funciona.

json
{
  "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 }
}
POST/api/v1/exams

Crea un examen y devuelve URLs presignadas para upload directo a R2.

CampoTipoRequeridoNotas
namestringNombre interno del examen (máx 200 chars).
exam_typestringcbct (default), radiografia, mesh, atm. Radiografia requiere Pro.
patient_namestringNombre del paciente. Aparece en el visor.
patient_idstringRUT, CPF, historia clínica.
birth_datestring ISOYYYY-MM-DD.
reasonstringMotivo / hallazgos solicitados.
expiration_daysnumberDías hasta expirar. null = nunca.
upload_modestring"files" (default) o "zip". En modo zip se devuelve 1 sola URL y luego se llama /process-zip.
filesarray[{ name, size }]. Requerido si upload_mode="files". Mínimo 1, máximo 5000.
zip_size_bytesnumberRequerido si upload_mode="zip". Máximo 1 GB (1073741824).
GET/api/v1/exams?limit=20&offset=0

Lista exámenes de tu cuenta, paginado. Default limit=20, máximo 100.

GET/api/v1/exams/{id}

Detalle de un examen. Incluye share_url, viewer_url, share_password y public_page_pin.

POST/api/v1/exams/{id}/confirm

Marca el examen como ready después de subir los archivos (modo files). Idempotente.

POST/api/v1/exams/{id}/process-zip

Para 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.

DELETE/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.

bash
curl -X DELETE "https://cbcthub.com/api/v1/exams/EXAM_ID" \
  -H "Authorization: Bearer $CBCTHUB_KEY"

# → { "deleted": true, "exam_id": "EXAM_ID" }
PATCH/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.

bash
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}'
POST/api/v1/exams/{id}/share

Enví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.

bash
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"}'
POST/api/v1/exams/{id}/extras

Inicia 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.

bash
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" }
POST/api/v1/exams/{id}/extras/{index}/confirm

Confirma que la toma extra terminó de subirse. Verifica los archivos en R2 y actualiza extra_series + storage. Body opcional: { label }. Idempotente. Scope: exams:write.

bash
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"}'
GET/api/v1/exams/{id}/access-pin

Devuelve 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.

bash
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" }
POST/api/v1/exams/{id}/access-pin

Rota 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.

bash
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" }
GET/api/v1/exams/{id}/embed

Devuelve el estado del embed (enabled, token, snippet, view_count). Scope: exams:read.

bash
curl "https://cbcthub.com/api/v1/exams/EXAM_ID/embed" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
POST/api/v1/exams/{id}/embed

Habilita el embed (genera token si no existe). Devuelve embed_url e iframe_snippet listo para pegar. Body opcional: { title, show_year, show_study_type }.

bash
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}'
DELETE/api/v1/exams/{id}/embed

Revoca 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.

bash
curl -X DELETE "https://cbcthub.com/api/v1/exams/EXAM_ID/embed" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
GET/api/v1/exams/{id}/thumbnail

Descarga 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.

bash
curl -o thumb.jpg "https://cbcthub.com/api/v1/exams/EXAM_ID/thumbnail" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
GET/api/v1/exams/{id}/patient-exams

Lista 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.

bash
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.

GET/api/v1/exams/{id}/consent

Devuelve el estado actual del consentimiento (status, signing_method, signed_at, hash). Si no existe aún, devuelve { consent: null }. Scope: exams:read.

bash
curl "https://cbcthub.com/api/v1/exams/EXAM_ID/consent" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
POST/api/v1/exams/{id}/consent

Inicializa (o re-inicializa si está pending) el consentimiento. Body opcional: { country_code, locale }. Si ya está firmado devuelve 409. Scope: exams:write.

bash
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"}'
POST/api/v1/exams/{id}/consent/sign-inperson

Firma 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.

bash
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
  }'
POST/api/v1/exams/{id}/consent/email

Enví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.

bash
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"}'
GET/api/v1/exams/{id}/consent/pdf?type=blank|signed

Descarga 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.

bash
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.

POST/api/v1/exams/{id}/report

Sube o reemplaza el PDF del informe. Body: { pdf_base64, filename? }.

GET/api/v1/exams/{id}/report

Devuelve URL presignada del PDF + datos de firma si está firmado.

DELETE/api/v1/exams/{id}/report

Elimina el informe. Idempotente: si no existe devuelve 200 igual.

POST/api/v1/exams/{id}/report/sign

Firma 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.

POST/api/v1/webhooks

Crea una subscription. Devuelve el secret UNA SOLA VEZ.

GET/api/v1/webhooks

Lista todas las subscriptions de la cuenta.

GET/api/v1/webhooks/{id}

Detalle de una subscription.

PATCH/api/v1/webhooks/{id}

Actualiza events, description o enabled.

DELETE/api/v1/webhooks/{id}

Elimina la subscription. La key revocada no se reactiva.

POST/api/v1/webhooks/{id}/test

Envía un ping de prueba al endpoint configurado.

GET/api/v1/webhooks/{id}/deliveries

Audit 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.

GET/api/v1/referrers

Lista todos los derivadores de la cuenta, ordenados alfabéticamente por nombre. Scope: referrers:read.

bash
curl "https://cbcthub.com/api/v1/referrers" \
  -H "Authorization: Bearer $CBCTHUB_KEY"

# → { "referrers": [{ "id": "rfr_...", "name": "Dra. Maria Lopez", "email": "...", "specialty": "..." }] }
POST/api/v1/referrers

Crea un derivador. Body: { name (requerido), email?, phone?, specialty? }.

bash
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"}'
GET/api/v1/referrers/{id}

Detalle de un derivador. Scope: referrers:read.

PATCH/api/v1/referrers/{id}

Actualiza parcialmente. Solo los campos enviados se modifican. Scope: referrers:write.

bash
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"}'
DELETE/api/v1/referrers/{id}

Elimina el derivador. Idempotente: si ya no existe devuelve already_deleted=true. Scope: referrers:write.

bash
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_*").

GET/api/v1/templates

Lista las plantillas visibles (defaults system + propias del usuario). Scope: templates:read.

bash
curl "https://cbcthub.com/api/v1/templates" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
POST/api/v1/templates

Crea 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.

bash
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" }
      ]
    }]
  }'
GET/api/v1/templates/{id}

Detalle de una plantilla (system o propia). Scope: templates:read.

bash
curl "https://cbcthub.com/api/v1/templates/tpl_xyz" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
PATCH/api/v1/templates/{id}

Actualiza una plantilla propia. Las defaults system devuelven 400. Scope: templates:write.

bash
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"}'
DELETE/api/v1/templates/{id}

Elimina una plantilla propia. Idempotente. Las defaults system devuelven 400. Scope: templates:write.

bash
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.

GET/api/v1/implants

Sin 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=.

bash
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.

GET/api/v1/support/tickets

Lista los 50 tickets más recientes de la cuenta (configurable con ?limit=). Scope: support:read.

bash
curl "https://cbcthub.com/api/v1/support/tickets?limit=20" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
POST/api/v1/support/tickets

Crea un ticket. Body: { subject, message, category?, exam_id? }. Categorías válidas: general, bug, billing, feature_request, account, viewer.

bash
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"
  }'
GET/api/v1/support/tickets/{id}

Detalle del ticket + lista de respuestas del admin (replies en orden cronológico). Scope: support:read.

bash
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.

GET/api/v1/activity

Log 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).

bash
curl "https://cbcthub.com/api/v1/activity?limit=20&action=exam.created" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
GET/api/v1/notifications

Lista 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.

bash
curl "https://cbcthub.com/api/v1/notifications?unread_only=true&limit=30" \
  -H "Authorization: Bearer $CBCTHUB_KEY"

# → { "notifications": [...], "unread_count": 4 }
POST/api/v1/notifications/{id}/mark-read

Marca una notificación como leída. Idempotente. Scope: account:write.

bash
curl -X POST "https://cbcthub.com/api/v1/notifications/ntf_xyz/mark-read" \
  -H "Authorization: Bearer $CBCTHUB_KEY"
POST/api/v1/notifications/mark-all-read

Marca TODAS las no leídas como leídas y devuelve marked_count. Scope: account:write.

bash
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

Los playgrounds de abajo ejecutan llamadas REALES contra tu cuenta. Si quieres probar sin afectar producción, usa una key cbct_test_* (créala en Ajustes → API, tab Sandbox).

GET /api/v1/exams

GET/api/v1/examsScope: exams:readPruébala ahora

Lista 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=0
Pega tu API key arriba para habilitar el botón.

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/exams

POST/api/v1/examsScope: exams:writePruébala ahora

Crea 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 →

Body JSON

Request

POST https://cbcthub.com/api/v1/exams
Pega tu API key arriba para habilitar el botón.

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.

GET /api/v1/exams/{id}

GET/api/v1/exams/{id}Scope: exams:readPruébala ahora

Detalle 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}
Pega tu API key arriba para habilitar el botón.

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

POST/api/v1/webhooksScope: webhooks:writePruébala ahora

Crea 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 →

Body JSON

Request

POST https://cbcthub.com/api/v1/webhooks
Pega tu API key arriba para habilitar el botón.

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/{id}/test

POST/api/v1/webhooks/{id}/testScope: webhooks:writePruébala ahora

Enví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

Body JSON

Request

POST https://cbcthub.com/api/v1/webhooks/{id}/test
Pega tu API key arriba para habilitar el botón.

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.