Grio AI Education Platform — API Design & Contracts
Document Version: 1.0 Last Updated: 2026-03-24 Audience: Development Team (Internal) Status: Approved for Implementation
1. API Design Principles
1.1 RESTful API Design
- All endpoints follow REST conventions with resource-based URLs
- HTTP methods: GET (read), POST (create), PATCH (update), DELETE (remove)
- Base URL:
https://api.grio.ai/api/v1/(production) - All endpoints are namespaced under
/api/v1/to support future versioning
1.2 JSON Request/Response Format
- All requests and responses use JSON encoding (UTF-8)
- Content-Type:
application/json(required for POST/PATCH) - Request bodies must be valid JSON objects
- Response bodies always contain data wrapped in root keys (e.g.,
data,error,pagination)
1.3 JWT Bearer Token Authentication
- Bearer token in Authorization header:
Authorization: Bearer {access_token} - Access tokens valid for 15 minutes (900 seconds)
- Refresh tokens valid for 7 days (604800 seconds)
- All protected endpoints require valid JWT in Authorization header
- Token payload includes:
user_id,email,role,school_id,exp,iat,iss
1.4 Consistent Error Response Format
All errors follow this standardized structure:
{
"error": {
"code": "ERROR_CODE_CONSTANT",
"message": "Human-readable error message",
"details": {
"field": ["specific error about this field"],
"nested_field": ["error details"]
}
}
}1.5 Versioning Strategy
- API versioning via URL path:
/api/v1/,/api/v2/(future) - Major breaking changes increment version number
- Deprecated endpoints return 310 with deprecation header:
X-API-Deprecation: v1 deprecated, use v2 - Clients must specify version explicitly in requests
1.6 Pagination Patterns
- Query parameters:
limit(default 20, max 100) andoffset(default 0) - Response includes pagination metadata:
{
"data": [...],
"pagination": {
"limit": 20,
"offset": 0,
"total": 150,
"has_next": true,
"has_previous": false
}
}2. Authentication & Authorization
2.1 POST /api/v1/auth/login/
Description: Authenticate user and return JWT token pair Authentication: None (public endpoint) Rate Limit: 5 requests per minute per IP
Request:
{
"email": "teacher@school.example.com",
"password": "SecurePassword123!",
"remember_me": false
}Response (200 OK):
{
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 42,
"email": "teacher@school.example.com",
"first_name": "John",
"last_name": "Doe",
"role": "TEACHER",
"school_id": 5,
"school_name": "St. Mary's Academy",
"avatar_url": "https://cdn.grio.ai/avatars/42.jpg"
},
"expires_in": 900
}
}Errors: - INVALID_CREDENTIALS (401): Email or password incorrect - ACCOUNT_DISABLED (403): User account is disabled - EMAIL_NOT_VERIFIED (403): Email verification required
2.2 POST /api/v1/auth/refresh/
Description: Refresh expired access token Authentication: Refresh token required
Request:
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Response (200 OK):
{
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 900
}
}2.3 GET /api/v1/auth/me/
Description: Fetch current authenticated user profile Authentication: Bearer token required
Response (200 OK):
{
"data": {
"id": 42,
"email": "teacher@school.example.com",
"first_name": "John",
"last_name": "Doe",
"role": "TEACHER",
"school_id": 5,
"permissions": ["view_class", "create_assignment", "view_analytics"],
"created_at": "2025-09-15T08:30:00Z",
"last_login": "2026-03-24T14:22:15Z"
}
}2.4 Role-Based Access Control (RBAC)
Roles and default permissions:
| Role | Endpoints Access | Features |
|---|---|---|
| Super Admin | All /api/admin/ | Manage schools, users, analytics |
| School Admin | /api/admin/school/*, /api/teachers/, /api/students/ | Manage school users, view reports |
| Teacher | /api/sessions/, /api/teachers/me/*, /api/ai/ | Manage classes, create assignments, AI tutoring |
| Student | /api/students/me/*, /api/sessions/, /api/ai/chat/ | Dashboard, learning history, AI chat |
| Independent Learner | /api/students/me/*, /api/sessions/, /api/ai/chat/ | Same as Student, no school affiliation |
3. Curriculum API
3.1 GET /api/v1/curricula/
Description: List all available curricula frameworks Authentication: Bearer token required
Query Parameters: - country (optional): Filter by country code (UG, ZM, KE) - active_only (default true): Include only active curricula
Response (200 OK):
{
"data": [
{
"id": 1,
"name": "Uganda National Curriculum Development Centre (NCDC)",
"country": "UG",
"short_code": "NCDC",
"description": "Official Ugandan primary and secondary curriculum",
"grades": ["P1", "P2", "P3", "P4", "P5", "P6", "P7", "S1", "S2", "S3", "S4", "S5", "S6"],
"is_active": true,
"created_at": "2025-01-01T00:00:00Z"
},
{
"id": 2,
"name": "Zambia Education Curriculum Framework (ZEC)",
"country": "ZM",
"short_code": "ZEC",
"grades": ["Grade1", "Grade2", "Grade3", "Grade4", "Grade5", "Grade6", "Grade7", "Grade8", "Grade9", "Grade10", "Grade11", "Grade12"],
"is_active": true,
"created_at": "2025-01-05T00:00:00Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 2,
"has_next": false,
"has_previous": false
}
}3.2 GET /api/v1/classes/
Description: List classes for a given curriculum Query Parameters: - curriculum_id (required): Curriculum ID - grade (optional): Filter by grade level
Response (200 OK):
{
"data": [
{
"id": 10,
"curriculum_id": 1,
"grade": "P6",
"name": "Primary 6",
"description": "Primary education, Level 6"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1,
"has_next": false,
"has_previous": false
}
}3.3 GET /api/v1/subjects/
Description: List subjects for a given class Query Parameters: - class_id (required): Class ID
Response (200 OK):
{
"data": [
{
"id": 101,
"class_id": 10,
"name": "English Language",
"code": "ENG",
"description": "English language and literature"
},
{
"id": 102,
"class_id": 10,
"name": "Mathematics",
"code": "MATH",
"description": "Mathematics fundamentals"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 2,
"has_next": false,
"has_previous": false
}
}3.4 GET /api/v1/topics/
Description: List topics for a class-subject-term combination Query Parameters: - class_subject_id (required): Class-subject ID - term_id (optional): Term ID (1, 2, 3) — defaults to current term
Response (200 OK):
{
"data": [
{
"id": 501,
"class_subject_id": 101,
"term_id": 1,
"name": "Introduction to Grammar",
"description": "Parts of speech, sentence structure",
"order": 1,
"lesson_count": 5
},
{
"id": 502,
"class_subject_id": 101,
"term_id": 1,
"name": "Reading Comprehension",
"description": "Strategies for understanding texts",
"order": 2,
"lesson_count": 4
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 2,
"has_next": false,
"has_previous": false
}
}3.5 GET /api/v1/lessons/
Description: List lessons within a topic Query Parameters: - topic_id (required): Topic ID
Response (200 OK):
{
"data": [
{
"id": 2001,
"topic_id": 501,
"title": "Parts of Speech Overview",
"order": 1,
"duration_minutes": 30,
"has_quiz": true,
"has_video": true
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1,
"has_next": false,
"has_previous": false
}
}3.6 GET /api/v1/lessons/{id}/content/
Description: Retrieve full lesson content including text, videos, quizzes Authentication: Bearer token required
Response (200 OK):
{
"data": {
"id": 2001,
"title": "Parts of Speech Overview",
"content_blocks": [
{
"type": "text",
"content": "Parts of speech are the grammatical categories..."
},
{
"type": "video",
"url": "https://media.grio.ai/videos/2001-intro.mp4",
"duration": 480,
"transcript": "In this video we explore..."
},
{
"type": "quiz",
"quiz_id": 3001,
"questions_count": 5
}
],
"estimated_completion_time": 30
}
}4. Classroom Session API
4.1 POST /api/v1/sessions/start/
Description: Start a new classroom or learning session Authentication: Bearer token required
Request:
{
"class_id": 10,
"subject_id": 101,
"topic_id": 501,
"lesson_id": 2001,
"mode": "teach",
"session_name": "P6 English - Lesson 1"
}Response (201 Created):
{
"data": {
"id": "sess_abc123xyz",
"class_id": 10,
"subject_id": 101,
"topic_id": 501,
"lesson_id": 2001,
"mode": "teach",
"status": "active",
"started_at": "2026-03-24T14:30:00Z",
"ends_at": null,
"participants": []
}
}4.2 GET /api/v1/sessions/{id}/
Description: Retrieve session details and current state Authentication: Bearer token required
Response (200 OK):
{
"data": {
"id": "sess_abc123xyz",
"class_id": 10,
"mode": "teach",
"status": "active",
"started_at": "2026-03-24T14:30:00Z",
"participants": [
{
"user_id": 101,
"name": "Alice Mwaka",
"role": "STUDENT",
"joined_at": "2026-03-24T14:30:15Z"
}
]
}
}4.3 PATCH /api/v1/sessions/{id}/mode/
Description: Switch session mode (teach/explore/practice/revision) Authentication: Bearer token required
Request:
{
"mode": "practice"
}Response (200 OK):
{
"data": {
"id": "sess_abc123xyz",
"mode": "practice",
"switched_at": "2026-03-24T14:45:00Z"
}
}4.4 POST /api/v1/sessions/{id}/end/
Description: End an active session Authentication: Bearer token required
Response (200 OK):
{
"data": {
"id": "sess_abc123xyz",
"status": "completed",
"ended_at": "2026-03-24T15:15:00Z",
"duration_minutes": 45,
"summary": {
"participants": 1,
"questions_asked": 12,
"interactions": 34
}
}
}4.5 GET /api/v1/sessions/active/
Description: Get active session for a class Query Parameters: - class_id (required): Class ID
Response (200 OK):
{
"data": {
"id": "sess_abc123xyz",
"class_id": 10,
"status": "active",
"mode": "teach",
"started_at": "2026-03-24T14:30:00Z"
}
}5. AI / Tutoring API
5.1 POST /api/v1/ai/chat/
Description: Send message to AI tutor and receive response Authentication: Bearer token required Rate Limit: 100 requests per minute per user
Request:
{
"session_id": "sess_abc123xyz",
"message": "How do I identify a noun in a sentence?",
"mode": "teach",
"context": {
"lesson_id": 2001,
"topic_id": 501
}
}Response (200 OK):
{
"data": {
"id": "msg_xyz789abc",
"session_id": "sess_abc123xyz",
"role": "assistant",
"message": "A noun is a word that names a person, place, thing, or idea...",
"type": "explanation",
"confidence": 0.95,
"metadata": {
"sources": ["lesson_2001", "content_segment_3"],
"follow_up_suggestions": [
"Can you give me examples?",
"How are nouns used in sentences?"
]
},
"timestamp": "2026-03-24T14:35:12Z"
}
}5.2 POST /api/v1/ai/lesson/start/
Description: Begin structured AI-led lesson delivery Authentication: Bearer token required
Request:
{
"session_id": "sess_abc123xyz",
"lesson_id": 2001,
"teaching_style": "socratic"
}Response (201 Created):
{
"data": {
"lesson_delivery_id": "ld_123abc",
"lesson_id": 2001,
"phase": "introduction",
"introduction_message": "Today we'll explore the parts of speech...",
"next_action": "present_content"
}
}5.3 POST /api/v1/ai/lesson/next/
Description: Advance to next step in lesson delivery Authentication: Bearer token required
Request:
{
"lesson_delivery_id": "ld_123abc",
"student_response": "I think nouns are action words"
}Response (200 OK):
{
"data": {
"phase": "content",
"content": "Actually, that describes verbs. Nouns are words that name things...",
"next_action": "ask_question",
"question": "Can you name a noun from your classroom?"
}
}5.4 POST /api/v1/ai/practice/question/
Description: Generate a practice question for a student Authentication: Bearer token required
Request:
{
"session_id": "sess_abc123xyz",
"topic_id": 501,
"difficulty": "medium",
"question_type": "multiple_choice"
}Response (200 OK):
{
"data": {
"question_id": "q_456def",
"question": "Which word is a noun? A) run, B) happy, C) dog, D) quickly",
"question_type": "multiple_choice",
"options": ["run", "happy", "dog", "quickly"],
"difficulty": "medium",
"estimated_time_seconds": 45
}
}5.5 POST /api/v1/ai/practice/validate/
Description: Validate student answer and provide feedback Authentication: Bearer token required
Request:
{
"question_id": "q_456def",
"student_answer": "C",
"session_id": "sess_abc123xyz"
}Response (200 OK):
{
"data": {
"is_correct": true,
"feedback": "Excellent! 'Dog' is a noun because it names a thing.",
"explanation": "Nouns are words that name people, places, things, or ideas...",
"points_awarded": 10,
"difficulty_adjustment": "increase",
"next_question_difficulty": "hard"
}
}5.6 GET /api/v1/ai/health/
Description: Check AI service health and readiness Authentication: None (health check)
Response (200 OK):
{
"data": {
"status": "healthy",
"version": "1.2.5",
"response_time_ms": 45,
"model_loaded": true,
"last_training": "2026-03-20T00:00:00Z"
}
}6. Student API
6.1 GET /api/v1/students/me/dashboard/
Description: Retrieve student dashboard with progress and learning status Authentication: Bearer token required
Response (200 OK):
{
"data": {
"student_id": 42,
"name": "Alice Mwaka",
"avatar": "https://cdn.grio.ai/avatars/42.jpg",
"class": "P6",
"school": "St. Mary's Academy",
"current_subjects": [
{
"id": 101,
"name": "English Language",
"progress_percent": 65,
"lessons_completed": 8,
"lessons_total": 12,
"last_studied": "2026-03-24T14:30:00Z"
},
{
"id": 102,
"name": "Mathematics",
"progress_percent": 42,
"lessons_completed": 5,
"lessons_total": 12,
"last_studied": "2026-03-23T16:00:00Z"
}
],
"continue_learning": {
"lesson_id": 2003,
"lesson_title": "Reading Comprehension - Techniques",
"subject": "English Language",
"progress": "partially_completed"
}
}
}6.2 GET /api/v1/students/me/progress/
Description: Get detailed progress data for a specific subject Query Parameters: - subject_id (required): Subject ID
Response (200 OK):
{
"data": {
"subject_id": 101,
"subject_name": "English Language",
"overall_progress": 65,
"topics": [
{
"id": 501,
"name": "Introduction to Grammar",
"progress": 100,
"lessons": [
{
"id": 2001,
"title": "Parts of Speech",
"status": "completed",
"score": 92
}
]
}
],
"milestones": [
{
"id": "m_001",
"title": "First 5 Lessons",
"unlocked_at": "2026-03-20T10:30:00Z"
}
]
}
}6.3 GET /api/v1/students/me/history/
Description: Retrieve learning history for Grio Continuity feature Query Parameters: - limit (default 20): Number of records - offset (default 0): Pagination offset
Response (200 OK):
{
"data": [
{
"id": "h_001",
"lesson_id": 2001,
"lesson_title": "Parts of Speech",
"subject": "English Language",
"class": "P6",
"accessed_at": "2026-03-24T14:30:00Z",
"duration_minutes": 25,
"status": "completed",
"score": 92
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 156,
"has_next": true,
"has_previous": false
}
}6.4 POST /api/v1/students/me/continue/
Description: Resume last learning session (Grio Continuity) Authentication: Bearer token required
Response (200 OK):
{
"data": {
"session_id": "sess_resumed_123",
"lesson_id": 2003,
"lesson_title": "Reading Comprehension - Techniques",
"subject": "English Language",
"last_position": {
"block_index": 2,
"timestamp": "2026-03-24T14:32:00Z"
},
"resume_url": "https://app.grio.ai/learn/session/sess_resumed_123"
}
}7. Teacher API
7.1 GET /api/v1/teachers/me/classes/
Description: List all classes taught by the current teacher Authentication: Bearer token required
Response (200 OK):
{
"data": [
{
"id": 10,
"name": "Primary 6 - Section A",
"grade": "P6",
"subject_ids": [101, 102],
"student_count": 35,
"schedule": {
"monday": ["09:00-10:30"],
"wednesday": ["11:00-12:30"]
}
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1,
"has_next": false,
"has_previous": false
}
}7.2 GET /api/v1/teachers/me/classes/{id}/students/
Description: Get students enrolled in a specific class Authentication: Bearer token required
Response (200 OK):
{
"data": [
{
"id": 42,
"name": "Alice Mwaka",
"enrollment_date": "2026-01-15T00:00:00Z",
"subjects": [101, 102],
"attendance_percent": 94
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 35,
"has_next": true,
"has_previous": false
}
}7.3 GET /api/v1/teachers/me/classes/{id}/performance/
Description: Retrieve aggregated performance data for a class Authentication: Bearer token required
Response (200 OK):
{
"data": {
"class_id": 10,
"class_name": "P6 - Section A",
"average_score": 78.5,
"average_progress": 62,
"top_performers": [
{
"student_id": 42,
"name": "Alice Mwaka",
"average_score": 92,
"progress": 85
}
],
"needs_support": [
{
"student_id": 55,
"name": "Bob Banda",
"average_score": 42,
"progress": 28
}
]
}
}7.4 POST /api/v1/teachers/me/assignments/
Description: Create new assignment for a class Authentication: Bearer token required
Request:
{
"class_id": 10,
"title": "Grammar Practice - Nouns and Verbs",
"description": "Practice identifying parts of speech",
"due_date": "2026-03-28T23:59:59Z",
"topic_ids": [501, 502]
}Response (201 Created):
{
"data": {
"id": "a_001",
"title": "Grammar Practice - Nouns and Verbs",
"class_id": 10,
"created_at": "2026-03-24T15:00:00Z",
"due_date": "2026-03-28T23:59:59Z",
"status": "active"
}
}8. Admin API
8.1 Admin CRUD Operations
Base Endpoints: - GET /api/v1/admin/schools/ — List all schools - POST /api/v1/admin/schools/ — Create school - GET /api/v1/admin/schools/{id}/ — School details - PATCH /api/v1/admin/schools/{id}/ — Update school - DELETE /api/v1/admin/schools/{id}/ — Delete school - GET /api/v1/admin/users/ — List users - POST /api/v1/admin/users/ — Create user - PATCH /api/v1/admin/users/{id}/ — Update user
Authentication: Bearer token required, Super Admin or School Admin role
8.2 GET /api/v1/admin/analytics/school/{id}/
Description: School-level analytics and KPIs Authentication: Bearer token (Super Admin or School Admin)
Response (200 OK):
{
"data": {
"school_id": 5,
"school_name": "St. Mary's Academy",
"total_students": 450,
"total_teachers": 25,
"average_engagement": 76,
"lessons_completed_today": 234,
"lessons_completed_week": 1456,
"top_subjects": [
{
"subject": "Mathematics",
"students_active": 150,
"average_score": 74.2
}
]
}
}8.3 GET /api/v1/admin/analytics/national/ (Future)
Description: National-level analytics across all schools Authentication: Bearer token (Super Admin only)
9. Webhook & Event Patterns
9.1 Session Completed Events
When a session ends, POST to registered webhook URL:
{
"event_type": "session.completed",
"event_id": "evt_abc123",
"timestamp": "2026-03-24T15:15:00Z",
"data": {
"session_id": "sess_abc123xyz",
"class_id": 10,
"duration_minutes": 45,
"participants": 1,
"completed_lessons": 1
}
}9.2 Student Progress Milestones
Triggered when student reaches milestone:
{
"event_type": "student.milestone_unlocked",
"timestamp": "2026-03-24T15:20:00Z",
"data": {
"student_id": 42,
"milestone": "First 5 Lessons Completed",
"milestone_id": "m_001",
"unlocked_at": "2026-03-24T15:20:00Z"
}
}9.3 Analytics Aggregation Triggers
Hourly aggregation of session and progress data for reporting
10. Error Handling
10.1 Standard Error Response Format
All errors return JSON with consistent structure:
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The requested lesson does not exist",
"details": {
"lesson_id": ["Lesson with ID 9999 not found"]
}
}
}10.2 HTTP Status Codes
| Code | Meaning | Use Case |
|---|---|---|
| 200 | OK | Successful GET, PATCH, DELETE |
| 201 | Created | Successful POST (resource created) |
| 400 | Bad Request | Invalid input, malformed JSON |
| 401 | Unauthorized | Missing or invalid JWT token |
| 403 | Forbidden | User lacks permission for resource |
| 404 | Not Found | Resource does not exist |
| 422 | Unprocessable Entity | Validation errors on data |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Unexpected server error |
10.3 Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
INVALID_CREDENTIALS | 401 | Email/password incorrect |
INVALID_TOKEN | 401 | JWT token is invalid or expired |
RESOURCE_NOT_FOUND | 404 | Requested resource doesn’t exist |
PERMISSION_DENIED | 403 | User doesn’t have required role/permission |
VALIDATION_ERROR | 422 | Input validation failed |
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
INTERNAL_SERVER_ERROR | 500 | Unexpected server error |
10.4 Rate Limiting Headers
All responses include rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 171127080011. Implementation Guidelines
11.1 Request/Response Best Practices
- Always include
Content-Type: application/jsonheader - Validate and sanitize all inputs server-side
- Never expose sensitive data (passwords, tokens) in responses
- Use consistent timestamp format: ISO 8601 (UTC)
- Support both camelCase and snake_case depending on client preference (documented separately)
11.2 Security Considerations
- All endpoints except
/auth/login/and/ai/health/require valid JWT - HTTPS mandatory for all production endpoints
- CORS configured to allow only approved frontend domains
- SQL injection prevention via ORM (Django ORM)
- CSRF protection for state-changing requests
- Log all API calls for audit trail (PII excluded from logs)
11.3 Documentation Links
- Full OpenAPI/Swagger spec:
/api/v1/docs/(auto-generated from code) - Postman collection available in project repository
- Client SDK libraries: Python, JavaScript (TypeScript), Dart (Flutter)
Document End Next Review Date: 2026-06-24