Version: 1.0
Base URL: http://sala.digital.local/api
Protocol: REST over HTTP
Authentication: None required (anonymous access)
This document defines the REST API specification for the SalaDigital mobile application and web interface. The system provides anonymous, open access to educational content while collecting anonymized usage analytics for donor reporting.
Note
All endpoints are accessible without authentication. This is an anonymous, open-access system designed for refugee camp environments where user registration is impractical.
GET /api/contentResponse:
{
"success": true,
"content": [
{
"id": "VID001",
"type": "video",
"title": "Khan Academy - Algebra Basics",
"description": "Introduction to algebraic expressions",
"duration": 720,
"size_bytes": 52428800,
"thumbnail_url": "/media/thumbnails/VID001.jpg",
"media_url": "/media/videos/VID001.mp4",
"language": "km",
"category": "mathematics"
},
{
"id": "PDF002",
"type": "document",
"title": "Grade 10 Physics Textbook",
"size_bytes": 10485760,
"thumbnail_url": "/media/thumbnails/PDF002.jpg",
"media_url": "/media/documents/PDF002.pdf",
"language": "km",
"category": "physics"
}
]
}GET /api/content/:idResponse:
{
"success": true,
"content": {
"id": "VID001",
"type": "video",
"title": "Khan Academy - Algebra Basics",
"description": "Introduction to algebraic expressions",
"duration": 720,
"size_bytes": 52428800,
"media_url": "/media/videos/VID001.mp4",
"language": "km",
"category": "mathematics",
"tags": ["algebra", "mathematics", "grade9"],
"related": ["VID002", "VID003"]
}
}Important
Usage tracking is completely anonymous - no personal information is collected. This data is used solely for donor reporting (e.g., "150 students watched Khan Academy videos this month").
POST /api/usage/:content_id
Content-Type: application/json
{
"event_type": "video_progress",
"progress": 0.75,
"timestamp": "2025-12-20T05:30:00Z",
"session_id": "abc123-def456" // Anonymous session identifier
}Event Types:
video_start- User started watchingvideo_progress- Periodic progress update (every 30s)video_complete- User finished viewingdocument_open- User opened PDFdocument_view- User viewed document page
Session ID:
- Generated by client app on first launch
- Stored locally (not linked to any personal info)
- Used only for deduplication and basic analytics
Response:
{
"success": true,
"saved": true,
"duplicate": false
}GET /api/stats/summaryResponse:
{
"success": true,
"period": "last_30_days",
"total_sessions": 1250,
"total_views": 3400,
"popular_content": [
{
"content_id": "VID001",
"title": "Khan Academy - Algebra Basics",
"view_count": 245,
"avg_completion": 0.78
}
]
}POST /api/sync
Content-Type: application/json
{
"session_id": "abc123-def456",
"events": [
{
"content_id": "VID001",
"event_type": "video_progress",
"progress": 0.25,
"timestamp": "2025-12-20T05:00:00Z"
},
{
"content_id": "VID001",
"event_type": "video_progress",
"progress": 0.50,
"timestamp": "2025-12-20T05:15:00Z"
},
{
"content_id": "VID001",
"event_type": "video_complete",
"progress": 1.0,
"timestamp": "2025-12-20T05:30:00Z"
}
]
}Response:
{
"success": true,
"processed": 3,
"duplicates": 0,
"errors": []
}Deduplication:
- Use
session_id+content_id+timestampas unique constraint - If duplicate event received, ignore (idempotent)
- Server tracks only aggregate statistics, not individual viewing history
GET /api/healthResponse:
{
"status": "healthy",
"timestamp": "2025-12-20T12:00:00Z",
"services": {
"database": "connected",
"storage": "available"
}
}GET /api/statusResponse:
{
"server_name": "SalaDigital Hub #1",
"version": "1.0.0",
"content_count": {
"videos": 150,
"documents": 75,
"quizzes": 30
},
"last_sync": "2025-12-15T10:00:00Z",
"storage": {
"total_gb": 900,
"used_gb": 125,
"available_gb": 775
}
}| HTTP Status | Code | Message | Description |
|---|---|---|---|
| 400 | INVALID_REQUEST |
Invalid request format | Missing required fields |
| 404 | NOT_FOUND |
Resource not found | Content ID doesn't exist |
| 429 | RATE_LIMIT |
Too many requests | Rate limit exceeded |
| 500 | SERVER_ERROR |
Internal server error | Contact administrator |
- Content Listing: 60 requests/minute per IP
- Usage Logging: 120 requests/minute per IP
- Batch Sync: 20 requests/minute per IP
- Static Media: No rate limit (handled by Nginx)
Response Header:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1703055600- App launches → Scans for WiFi networks matching
SalaDigital% - Connects to WiFi → Gets IP via DHCP (10.10.10.x)
- Probe request →
GET http://sala.digital.local/api/status - If success → Show content library
- If failure → Show "No hub detected"
Mobile app should:
- Generate anonymous
session_idon first launch (UUID v4) - Queue usage events in local SQLite database
- Sync queue when reconnected to hub WiFi
- Download content to device storage for offline viewing
- No authentication required - instant access to all content
// Generated client-side (no server registration)
session_id = uuid.v4() // Example: "550e8400-e29b-41d4-a716-446655440000"
// Stored in app local storage
// NOT linked to any personal information
// Used only for deduplication and basic analytics
-- See infrastructure-design.md Section 6.2 for full schema
CREATE TABLE usage_logs (
id SERIAL PRIMARY KEY,
session_id VARCHAR(100), -- Anonymous session
content_id VARCHAR(100),
event_type VARCHAR(50),
progress DECIMAL(5,4),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(session_id, content_id, event_type, created_at)
);Document Status: Ready for implementation
Last Updated: 2025-12-20
Contact: API Team