API — Dashboard REST
The dashboard API exposes reads over the processed data in SoftSys Edu Analytics. It is used internally by the web dashboard, but it can also be consumed from your own integrators (backends, BI tools, mobile apps) that want to build their own experience on top of tenant data.
Base URL
Section titled “Base URL”https://api.softsysanalytics.comAuthentication
Section titled “Authentication”The dashboard API accepts two modes:
Authorization: Bearer <jwt>— session JWT obtained via SSO exchange from the Moodle plugin.X-SSEA-Key: ssea_<key>— tenant-scoped API key, for service-to-service calls (backend integrators).
Bearer JWT is the mode the web dashboard (React) uses. X-SSEA-Key is the recommended mode for your own integrators.
SSO (token exchange)
Section titled “SSO (token exchange)”POST /v1/auth/sso
Section titled “POST /v1/auth/sso”Exchange a token signed by the Moodle plugin for a dashboard session JWT. Used by the standard SSO flow; regular integrators do not need it.
Request:{ "token": "<jwt_signed_by_moodle_plugin>"}
Response 200:{ "ok": true, "data": { "token": "<dashboard_session_jwt>", "tenant_id": "<tenant_uuid>", "expires_at": "2026-04-13T16:30:00Z" }}Overview (tenant summary)
Section titled “Overview (tenant summary)”GET /v1/tenants/:id/overview
Section titled “GET /v1/tenants/:id/overview”High-level metrics for the tenant. Used by the dashboard home.
Query params: days (int, default 30) — window in days.
curl -H "X-SSEA-Key: ssea_<your_key>" \ "https://api.softsysanalytics.com/v1/tenants/<tenant_id>/overview?days=30"Response 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
Section titled “GET /v1/tenants/:id/overview/trends”Time series for trend charts (events per day/hour).
GET /v1/tenants/:id/users
Section titled “GET /v1/tenants/:id/users”Paginated list of users with their activity summary.
Query params:
limit(int, default 50, max 200)offset(int, default 0)risk_level(optional):low | medium | high | critical
Response 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
Section titled “GET /v1/tenants/:id/users/:userId/profile”Detailed profile for a user with activity history and daily timeline.
GET /v1/tenants/:id/activity/top
Section titled “GET /v1/tenants/:id/activity/top”Top users and courses by event volume (short list for widgets).
GET /v1/tenants/:id/activity/timeline
Section titled “GET /v1/tenants/:id/activity/timeline”Activity heatmap by hour and day of week.
Courses
Section titled “Courses”GET /v1/tenants/:id/courses
Section titled “GET /v1/tenants/:id/courses”List of courses with engagement metrics.
Response 200:
[ { "course_id": "course_1", "fullname": "Introduction to Python", "shortname": "CS101", "active_users": 87, "total_events": 3421, "avg_time_mins": 42.1 }]At-risk students
Section titled “At-risk students”GET /v1/tenants/:id/at-risk
Section titled “GET /v1/tenants/:id/at-risk”List of at-risk students with score and factors.
Query params:
course_id(optional): filter by course.risk_level(optional): filter by level.limit,offset,sort(e.g.score_desc).
Response 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
Section titled “POST /v1/tenants/:id/at-risk/:userId/acknowledge”Mark a student’s risk flag as seen/handled. Useful for a teacher to note they already took action.
Response 200:
{ "ok": true }Alerts
Section titled “Alerts”GET /v1/tenants/:id/alerts
Section titled “GET /v1/tenants/:id/alerts”List of active alerts (inactivity, performance drops, completion milestones, etc.).
Query params: include_acknowledged (bool, default false).
Response 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
Section titled “POST /v1/tenants/:id/alerts/:alertId/acknowledge”Acknowledge a single alert.
POST /v1/tenants/:id/alerts/acknowledge-all
Section titled “POST /v1/tenants/:id/alerts/acknowledge-all”Acknowledge all pending alerts for the tenant.
Enrollments, grades, and completion
Section titled “Enrollments, grades, and completion”GET /v1/tenants/:id/enrollment/summary
Section titled “GET /v1/tenants/:id/enrollment/summary”Aggregated enrollment stats by course and role.
GET /v1/tenants/:id/grades/summary
Section titled “GET /v1/tenants/:id/grades/summary”Grade distribution (avg, min, max) at tenant level.
GET /v1/tenants/:id/courses/:courseId/grades
Section titled “GET /v1/tenants/:id/courses/:courseId/grades”Grade analytics for a specific course.
GET /v1/tenants/:id/completion/summary
Section titled “GET /v1/tenants/:id/completion/summary”Aggregated completion rate by course.
GET /v1/tenants/:id/courses/:courseId/completion
Section titled “GET /v1/tenants/:id/courses/:courseId/completion”Completion by activity within a course.
Async reports
Section titled “Async reports”For exports that may take time (e.g. retention cohorts, performance analysis), SSEA offers an async job flow: create a job, poll its status, and download the artifact when it’s ready.
POST /v1/tenants/:id/reports/jobs
Section titled “POST /v1/tenants/:id/reports/jobs”Create a report job.
Request body:
{ "report_type": "retention_cohort", "format": "csv", "params": { "date_from": "2026-01-01", "date_to": "2026-03-31", "max_weeks": 12 }}report_type values:
retention_cohort— retention by enrollment cohort.activity_funnel— completion funnel by activity.teacher_course_health— course health from the teacher’s perspective.student_learning_profile— aggregated student profile.assessment_performance— assessment performance.early_warning_summary— early warning summary.
Supported formats: csv (default) or json.
Response 202:
{ "job_id": "job_xyz", "status": "pending", "requested_at": "2026-04-13T14:00:00Z"}GET /v1/tenants/:id/reports/jobs
Section titled “GET /v1/tenants/:id/reports/jobs”List the tenant’s jobs (options: status, limit, offset).
GET /v1/tenants/:id/reports/jobs/:jobId
Section titled “GET /v1/tenants/:id/reports/jobs/:jobId”Check a job’s status.
Response 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": "..." } ]}Possible states: pending, running, completed, failed, expired.
GET /v1/tenants/:id/reports/jobs/:jobId/download
Section titled “GET /v1/tenants/:id/reports/jobs/:jobId/download”Download the report artifact. Redirects to a signed URL (Cloudflare R2) or returns the content inline depending on configuration.
Monitors (saved watchlists)
Section titled “Monitors (saved watchlists)”Monitors are user-saved filters — useful for tracking a specific group of students or courses.
GET /v1/tenants/:id/monitors— list monitors.POST /v1/tenants/:id/monitors— create a new monitor.DELETE /v1/tenants/:id/monitors/:id— delete a monitor.
Viewer (Moodle-embedded reads)
Section titled “Viewer (Moodle-embedded reads)”Viewer endpoints let a student or teacher see their own data from an iframe inside Moodle, without holding a dashboard JWT. They require the X-SSEA-Viewer-Context and X-SSEA-Viewer-Signature headers (see Security & privacy).
Student
Section titled “Student”GET /v1/tenants/:id/viewer/student/:moodleUserId/profile— student’s profile.GET /v1/tenants/:id/viewer/student/:moodleUserId/progress— progress by course and activity.GET /v1/tenants/:id/viewer/student/:moodleUserId/alerts— the student’s own alerts.
Teacher / instructor
Section titled “Teacher / instructor”GET /v1/tenants/:id/viewer/teacher/:moodleUserId/courses/health— health of their courses, at-risk counts.GET /v1/tenants/:id/viewer/teacher/:moodleUserId/students/at-risk— at-risk students in their courses.
Branding
Section titled “Branding”GET /v1/branding
Section titled “GET /v1/branding”Public branding — resolved by the Host header (useful for white-label with custom domain). No authentication required.
Response 200:
{ "logo_url": "<r2_signed_url>", "primary_color": "#4f46e5", "product_name": "Acme Analytics"}GET /v1/tenants/:id/branding
Section titled “GET /v1/tenants/:id/branding”Branding for the authenticated tenant.
Response codes
Section titled “Response codes”| HTTP | Meaning |
|---|---|
200 | OK. |
204 | CORS preflight. |
400 | Invalid request or query params. |
401 | Missing, invalid, or expired token. |
403 | Cross-tenant — the authenticated token does not match the path tenant. |
404 | Resource not found. |
422 | Payload validation failed. |
429 | Rate limit exceeded. |
503 | Worker misconfigured. |
Error format:
{ "ok": false, "error": "Human-readable message", "code": "ERROR_CODE"}