API — Dashboard REST
La API del dashboard expone lecturas sobre los datos procesados en SoftSys Edu Analytics. La usa internamente el dashboard web, pero también puede consumirse desde integradores propios (backends, BI tools, apps móviles) que quieran construir su propia experiencia sobre los datos del tenant.
Base URL
Sección titulada «Base URL»https://api.softsysanalytics.comAutenticación
Sección titulada «Autenticación»La API del dashboard acepta dos modos:
Authorization: Bearer <jwt>— JWT de sesión obtenido por intercambio SSO desde el plugin Moodle.X-SSEA-Key: ssea_<key>— clave API tenant-scoped, para llamadas servicio-a-servicio (integradores backend).
El modo Bearer JWT es el que usa el dashboard web (React). El modo X-SSEA-Key es el recomendado para integradores propios.
SSO (intercambio de tokens)
Sección titulada «SSO (intercambio de tokens)»POST /v1/auth/sso
Sección titulada «POST /v1/auth/sso»Intercambia un token firmado desde el plugin Moodle por un JWT de sesión del dashboard. Usado por el flujo SSO estándar; los integradores normales no lo necesitan.
Request:{ "token": "<jwt_firmado_por_plugin_moodle>"}
Response 200:{ "ok": true, "data": { "token": "<jwt_sesion_dashboard>", "tenant_id": "<tenant_uuid>", "expires_at": "2026-04-13T16:30:00Z" }}Overview (resumen del tenant)
Sección titulada «Overview (resumen del tenant)»GET /v1/tenants/:id/overview
Sección titulada «GET /v1/tenants/:id/overview»Métricas de alto nivel del tenant. Usado por el home del dashboard.
Query params: days (int, por defecto 30) — ventana en días.
curl -H "X-SSEA-Key: ssea_<your_key>" \ "https://api.softsysanalytics.com/v1/tenants/<tenant_id>/overview?days=30"Respuesta 200:
{ "tenant_id": "<tenant_uuid>", "period_days": 30, "total_events": 45231, "active_users": 312, "total_sessions": 1842, "avg_time_spent_mins": 34.5, "at_risk_count": 17}GET /v1/tenants/:id/overview/trends
Sección titulada «GET /v1/tenants/:id/overview/trends»Series temporales para gráficos de tendencia (eventos por día/hora).
Usuarios
Sección titulada «Usuarios»GET /v1/tenants/:id/users
Sección titulada «GET /v1/tenants/:id/users»Lista paginada de usuarios con su resumen de actividad.
Query params:
limit(int, por defecto 50, máx 200)offset(int, por defecto 0)risk_level(opcional):low | medium | high | critical
Respuesta 200:
[ { "user_id": "user_1", "username": "jdoe", "full_name": "Jane Doe", "last_seen_at": "2026-03-23T18:30:00Z", "total_events": 142, "total_time_mins": 87, "risk_level": "low", "risk_score": 12.4 }]GET /v1/tenants/:id/users/:userId/profile
Sección titulada «GET /v1/tenants/:id/users/:userId/profile»Perfil detallado de un usuario con histórico de actividad y timeline diaria.
GET /v1/tenants/:id/activity/top
Sección titulada «GET /v1/tenants/:id/activity/top»Top de usuarios y cursos por volumen de eventos (lista corta para widgets).
GET /v1/tenants/:id/activity/timeline
Sección titulada «GET /v1/tenants/:id/activity/timeline»Heatmap de actividad por hora y día de la semana.
GET /v1/tenants/:id/courses
Sección titulada «GET /v1/tenants/:id/courses»Lista de cursos con métricas de engagement.
Respuesta 200:
[ { "course_id": "course_1", "fullname": "Introduction to Python", "shortname": "CS101", "active_users": 87, "total_events": 3421, "avg_time_mins": 42.1 }]Estudiantes en riesgo (At-Risk)
Sección titulada «Estudiantes en riesgo (At-Risk)»GET /v1/tenants/:id/at-risk
Sección titulada «GET /v1/tenants/:id/at-risk»Lista de estudiantes en riesgo con su score y factores.
Query params:
course_id(opcional): filtrar por curso.risk_level(opcional): filtrar por nivel.limit,offset,sort(p. ej.score_desc).
Respuesta 200:
[ { "user_id": "user_42", "username": "mjones", "full_name": "Mike Jones", "risk_level": "high", "risk_score": 73.2, "last_seen_at": "2026-03-15T10:00:00Z", "days_inactive": 9, "completion_pct": 18.5, "acknowledged": false }]POST /v1/tenants/:id/at-risk/:userId/acknowledge
Sección titulada «POST /v1/tenants/:id/at-risk/:userId/acknowledge»Marcar como visto/gestionado el flag de riesgo de un estudiante. Útil para que el profesor indique que ya tomó acción.
Respuesta 200:
{ "ok": true }Alertas
Sección titulada «Alertas»GET /v1/tenants/:id/alerts
Sección titulada «GET /v1/tenants/:id/alerts»Lista de alertas activas (inactividad, caídas de rendimiento, hitos de completitud, etc.).
Query params: include_acknowledged (bool, por defecto false).
Respuesta 200:
[ { "alert_id": "alrt_abc123", "user_id": "user_42", "alert_type": "inactivity", "severity": "high", "message": "User has been inactive for 9 days", "triggered_at": "2026-03-24T01:00:00Z", "acknowledged": false }]POST /v1/tenants/:id/alerts/:alertId/acknowledge
Sección titulada «POST /v1/tenants/:id/alerts/:alertId/acknowledge»Acusar recibo de una alerta individual.
POST /v1/tenants/:id/alerts/acknowledge-all
Sección titulada «POST /v1/tenants/:id/alerts/acknowledge-all»Acusar recibo de todas las alertas pendientes del tenant.
Matrículas, calificaciones y completitud
Sección titulada «Matrículas, calificaciones y completitud»GET /v1/tenants/:id/enrollment/summary
Sección titulada «GET /v1/tenants/:id/enrollment/summary»Estadísticas de matrícula agregadas por curso y rol.
GET /v1/tenants/:id/grades/summary
Sección titulada «GET /v1/tenants/:id/grades/summary»Distribución de calificaciones (promedio, mínimo, máximo) a nivel tenant.
GET /v1/tenants/:id/courses/:courseId/grades
Sección titulada «GET /v1/tenants/:id/courses/:courseId/grades»Analítica de calificaciones para un curso específico.
GET /v1/tenants/:id/completion/summary
Sección titulada «GET /v1/tenants/:id/completion/summary»Tasa de completitud agregada por curso.
GET /v1/tenants/:id/courses/:courseId/completion
Sección titulada «GET /v1/tenants/:id/courses/:courseId/completion»Completitud por actividad dentro de un curso.
Reportes asíncronos
Sección titulada «Reportes asíncronos»Para exports que pueden tardar (p. ej. cohortes de retención, análisis de performance), SSEA ofrece un flujo de jobs asíncronos: creas un job, consultas su estado, y descargas el artefacto cuando está listo.
POST /v1/tenants/:id/reports/jobs
Sección titulada «POST /v1/tenants/:id/reports/jobs»Crear un job de reporte.
Request body:
{ "report_type": "retention_cohort", "format": "csv", "params": { "date_from": "2026-01-01", "date_to": "2026-03-31", "max_weeks": 12 }}Valores de report_type:
retention_cohort— retención por cohorte de matrícula.activity_funnel— embudo de completitud por actividad.teacher_course_health— salud del curso desde la óptica del profesor.student_learning_profile— perfil agregado del estudiante.assessment_performance— rendimiento en evaluaciones.early_warning_summary— resumen de alertas tempranas.
Formatos soportados: csv (por defecto) o json.
Respuesta 202:
{ "job_id": "job_xyz", "status": "pending", "requested_at": "2026-04-13T14:00:00Z"}GET /v1/tenants/:id/reports/jobs
Sección titulada «GET /v1/tenants/:id/reports/jobs»Listar jobs del tenant (opciones: status, limit, offset).
GET /v1/tenants/:id/reports/jobs/:jobId
Sección titulada «GET /v1/tenants/:id/reports/jobs/:jobId»Consultar estado de un job.
Respuesta 200:
{ "job_id": "job_xyz", "status": "completed", "requested_at": "2026-04-13T14:00:00Z", "completed_at": "2026-04-13T14:01:23Z", "artifacts": [ { "format": "csv", "url": "<signed_url>", "expires_at": "..." } ]}Estados posibles: pending, running, completed, failed, expired.
GET /v1/tenants/:id/reports/jobs/:jobId/download
Sección titulada «GET /v1/tenants/:id/reports/jobs/:jobId/download»Descarga el artefacto del reporte. Redirige a una URL firmada (Cloudflare R2) o devuelve el contenido inline según configuración.
Monitores (watchlists guardadas)
Sección titulada «Monitores (watchlists guardadas)»Los monitores son filtros guardados por el usuario — útiles para seguir un grupo de estudiantes o cursos específicos.
GET /v1/tenants/:id/monitors— listar monitores.POST /v1/tenants/:id/monitors— crear monitor nuevo.DELETE /v1/tenants/:id/monitors/:id— eliminar monitor.
Viewer (lectura embebida desde Moodle)
Sección titulada «Viewer (lectura embebida desde Moodle)»Los endpoints de viewer permiten que un estudiante o profesor vea sus propios datos desde una iframe dentro de Moodle, sin tener un JWT de dashboard. Requieren las cabeceras X-SSEA-Viewer-Context y X-SSEA-Viewer-Signature (ver Seguridad y privacidad).
Estudiante
Sección titulada «Estudiante»GET /v1/tenants/:id/viewer/student/:moodleUserId/profile— perfil del estudiante.GET /v1/tenants/:id/viewer/student/:moodleUserId/progress— progreso por curso y actividad.GET /v1/tenants/:id/viewer/student/:moodleUserId/alerts— alertas propias.
Profesor / instructor
Sección titulada «Profesor / instructor»GET /v1/tenants/:id/viewer/teacher/:moodleUserId/courses/health— salud de sus cursos, contadores de at-risk.GET /v1/tenants/:id/viewer/teacher/:moodleUserId/students/at-risk— estudiantes en riesgo en sus cursos.
Branding
Sección titulada «Branding»GET /v1/branding
Sección titulada «GET /v1/branding»Branding público — se resuelve por la cabecera Host (útil para white-label con dominio personalizado). Sin autenticación.
Respuesta 200:
{ "logo_url": "<r2_signed_url>", "primary_color": "#4f46e5", "product_name": "Acme Analytics"}GET /v1/tenants/:id/branding
Sección titulada «GET /v1/tenants/:id/branding»Branding del tenant autenticado.
Códigos de respuesta
Sección titulada «Códigos de respuesta»| HTTP | Significado |
|---|---|
200 | OK. |
204 | CORS preflight. |
400 | Request o query params inválidos. |
401 | Token ausente, inválido o expirado. |
403 | Cross-tenant — el token autenticado no coincide con el tenant del path. |
404 | Recurso no encontrado. |
422 | Validación de payload falló. |
429 | Rate limit excedido. |
503 | Worker mal configurado. |
Formato de error:
{ "ok": false, "error": "Mensaje legible", "code": "ERROR_CODE"}