RADIA — Referencia de API
Última actualización: 2026-04-14
Base URL: https://radia.cadences.app/api
Autenticación: Authorization: Bearer <JWT> (salvo endpoints /public/)
Todas las respuestas incluyen Access-Control-Allow-Origin: *.
Autenticación
POST /api/auth/google
Login con Google OAuth2. Verifica el id_token contra JWKS de Google, crea o actualiza el usuario en D1, y retorna un JWT de sesión.
Body:
{
"token": "eyJhbGciOiJSUzI1NiIs..."
}
Respuesta 200:
{
"success": true,
"jwt": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "usr_abc123",
"email": "[email protected]",
"name": "Dr. García",
"avatar_url": "https://...",
"plan": "pro",
"studies_quota": 50
}
}
Estudios
GET /api/studies
Lista todos los estudios del usuario autenticado con conteo de hallazgos.
Respuesta 200:
{
"studies": [
{
"id": "study_xyz",
"patient_name": "DOE^JOHN",
"modality": "CT",
"slice_count": 280,
"status": "analyzed",
"findings_count": 12,
"created_at": "2026-04-10T14:30:00Z"
}
]
}
POST /api/studies/upload
Inicia subida de un estudio. Comprueba cuota del usuario. Si hay un estudio en progreso con mismos datos, lo reanuda.
Body:
{
"metadata": {
"patientName": "DOE^JOHN",
"modality": "CT",
"studyDate": "20260410",
"rows": 512,
"columns": 512,
"windowCenter": 400,
"windowWidth": 1500,
"sliceThickness": 0.3
},
"sliceCount": 280
}
Respuesta 200:
{
"success": true,
"studyId": "study_xyz",
"r2Prefix": "usr_abc/study_xyz/",
"resumed": false
}
GET /api/studies/:id
Obtiene estudio completo con hallazgos, informes y datos del paciente.
Respuesta 200:
{
"study": { "id": "study_xyz", "...": "..." },
"findings": [...],
"reports": [...]
}
POST /api/studies/:id
Acciones sobre el estudio.
Finalizar subida:
{ "action": "finalize" }
Guardar comentario de doctor:
{
"action": "comment",
"findingId": "find_abc",
"comment": "Correlacionar con historia clínica"
}
PATCH /api/studies/:id
Actualizar datos del paciente.
{
"patientName": "DOE^JOHN",
"patientDob": "19800115",
"patientSex": "M"
}
PUT /api/studies/:id
Actualizar un hallazgo (confirmar, rechazar, notas).
{
"findingId": "find_abc",
"confirmed": 1,
"notes": "Confirmado en exploración clínica"
}
Valores de confirmed: 1 = doctor confirmó, -1 = doctor rechazó, 0 = pendiente, -1 = oculto del informe (hidden)
El campo
user_confirmedacepta cualquier valor numérico. El valor-1se usa tanto para rechazo como para ocultar hallazgos del informe (hidden). Los hallazgos conuser_confirmed = -1no aparecen en el panel de hallazgos por defecto.
DELETE /api/studies/:id
Eliminar estudio, hallazgos, o informe.
Eliminar estudio completo:
{ "deleteStudy": true }
Eliminar informe específico:
{ "reportId": "rpt_abc" }
Eliminar hallazgo individual (o grupo de hallazgos):
{ "findingId": "find_abc" }
Si el hallazgo forma parte de un grupo deduplicado, el frontend envía los IDs agrupados y el backend elimina todos los hallazgos del grupo.
Análisis IA
POST /api/analysis/scan360
Análisis completo del estudio por lotes. Se controla en 3 fases desde el frontend.
Fase 1: Init
{
"studyId": "study_xyz",
"action": "init",
"asteroidMode": false,
"reason": "Análisis inicial del estudio"
}
reason(opcional): Texto libre que el doctor puede proporcionar. Si incluye el prefijo[COMPLEMENT], el backend preserva los hallazgos existentes y añade nuevos (complementar análisis). Si incluye[REVIEW], también preserva hallazgos y ejecuta una revisión crítica. Sin prefijo o con[DELETE], se eliminan todos los hallazgos previos antes de re-analizar.
Respuesta:
{
"success": true,
"jobId": "job_abc",
"sliceIndices": [0, 8, 16, 24, ...],
"totalBatches": 5,
"sampleRate": 8,
"asteroidMode": false
}
Con
asteroidMode: true,sampleRatebaja a 2 (cada 2 cortes vs cada 8).
Fase 2: Batch
{
"studyId": "study_xyz",
"action": "batch",
"jobId": "job_abc",
"batchIndex": 0
}
Respuesta:
{
"success": true,
"batchFindings": 3,
"processedSlices": 8,
"totalProcessed": 8
}
En modo Asteroide, cada corte se analiza con Gemini + Groq Specialist en paralelo. Los hallazgos se deduplican por
category+location+slicey los de consenso multi-modelo reciben un boost de confianza ×1.15.
Fase 3: Finalize
{
"studyId": "study_xyz",
"action": "finalize",
"jobId": "job_abc"
}
Respuesta:
{
"success": true,
"totalFindings": 15,
"impression": "Estudio CBCT dental con 15 hallazgos...",
"asteroidMode": false
}
En modo Asteroide, la finalización ejecuta automáticamente deep analysis en los 5 hallazgos de mayor severidad.
POST /api/analysis/deep
Análisis profundo de un hallazgo específico. Lee ±10 cortes alrededor del hallazgo.
{
"studyId": "study_xyz",
"findingId": "find_abc"
}
Respuesta:
{
"success": true,
"deepAnalysis": {
"detailed_assessment": "...",
"differential_diagnosis": ["...", "..."],
"recommended_actions": ["...", "..."],
"clinical_significance": "...",
"confidence_revision": 0.85
}
}
POST /api/analysis/pointask
Análisis localizado por coordenada (click en el visor).
{
"studyId": "study_xyz",
"sliceIndex": 142,
"x": 0.45,
"y": 0.62,
"imageBase64": "data:image/png;base64,..."
}
x,yson coordenadas normalizadas (0-1).imageBase64es opcional — si no se envía, se lee el thumbnail de R2.
Respuesta:
{
"success": true,
"analysis": "En esta zona se observa...",
"model": "gemini-2.5-flash",
"finding": {
"title": "Lesión periapical",
"severity": "suspicious",
"confidence": 0.78
}
}
POST /api/analysis/viewport
Captura y analiza la vista actual del visor 3D o MPR.
{
"studyId": "study_xyz",
"imageBase64": "data:image/png;base64,...",
"camera": {
"rotX": 15.2,
"rotY": -30.5,
"dist": 2.8,
"mode": "bone"
}
}
Respuesta:
{
"success": true,
"analysis": "Desde esta perspectiva volumétrica...",
"viewportKey": "usr_abc/study_xyz/viewports/vp_123.png"
}
POST /api/analysis/suggestions
Sintetiza todos los hallazgos en un plan de tratamiento.
{
"studyId": "study_xyz"
}
Respuesta:
{
"success": true,
"suggestions": {
"summary": "Paciente con múltiples hallazgos...",
"treatment_plan": [...],
"priority_actions": [...],
"follow_up": "..."
}
}
Chat
POST /api/chat
Conversación multimodal sobre el estudio.
{
"studyId": "study_xyz",
"messages": [
{ "role": "user", "content": "¿Qué opinas de la zona periapical del 46?" }
],
"imageBase64": "data:image/png;base64,...",
"sliceNumber": 142
}
Respuesta:
{
"success": true,
"message": {
"role": "assistant",
"content": "En el corte 142, la zona periapical del 46..."
},
"model": "gemini-2.5-flash"
}
Compartir
GET /api/share?studyId=xxx
Obtiene enlace de compartir existente.
POST /api/share
Crea enlace de compartir.
{
"studyId": "study_xyz",
"expiresInDays": 7
}
Respuesta:
{
"success": true,
"token": "share_abc123def456",
"url": "https://radia.cadences.app/shared?token=share_abc123def456",
"expiresAt": "2026-04-20T14:30:00Z"
}
DELETE /api/share
Revoca enlace de compartir.
{ "studyId": "study_xyz" }
Endpoints públicos (sin JWT)
GET /api/public/study?token=xxx
Obtiene estudio y hallazgos via share token. Verifica expiración.
GET /api/public/dicom/:key?token=xxx
Proxy de archivos DICOM/PNG vía share token.
Endpoints de colaborador (JWT + share token)
GET /api/collab/study?token=xxx
Igual que público pero requiere JWT (usuario autenticado).
GET /api/collab/dicom/:key?token=xxx
Proxy DICOM para colaboradores autenticados.
DICOM Proxy
GET /api/dicom/:key
Proxy autenticado para archivos en R2. Verifica que el key pertenece al usuario.
Headers de respuesta:
Content-Type: application/dicom (o image/png para thumbs)
Cache-Control: public, max-age=31536000
Códigos de error
| Código | Significado |
|---|---|
| 400 | Parámetros faltantes o inválidos |
| 401 | JWT ausente, expirado o inválido |
| 403 | Sin permiso para este recurso |
| 404 | Estudio/hallazgo no encontrado |
| 410 | Enlace compartido expirado |
| 413 | Imagen demasiado grande (>6MB) |
| 429 | Demasiadas peticiones |
| 500 | Error interno del servidor |
Formato de error:
{
"error": "Mensaje descriptivo en español"
}