Version: 1.1 Date: April 29, 2026 Target Audience: Next.js Frontend Developers (Teacher Views) Backend: Django REST Framework
The API uses JWT tokens stored in HTTP-Only cookies.
POST /api/accounts/login/. The server sets two cookies:
access_token — short-lived (30 minutes)refresh_token — long-lived (7 days)Authorization header needed.Authorization: Bearer <token> header.POST /api/accounts/token/refresh/. The server reads the refresh token from the cookie, generates new tokens, and blacklists the old refresh token.POST /api/accounts/logout/. The server blacklists the refresh token and clears both cookies.Inactive User Blocking:
| Status | Condition | Response Body |
|---|---|---|
401 Unauthorized |
Not authenticated (missing/invalid token) | {"detail": "Authentication credentials were not provided."} |
401 Unauthorized |
Token expired | {"error": "Invalid or expired refresh token"} |
403 Forbidden |
Insufficient permissions | {"detail": "You do not have permission to perform this action."} |
404 Not Found |
Object does not exist | {"detail": "Not found."} or {"error": "String"} |
500 Internal Server Error |
Unexpected server error | {"detail": "Internal server error"} |
List endpoints that support pagination return this wrapper by default:
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": "Array[Object]"
}
Bypass pagination by adding ?all=true to get all results in a single response:
{
"count": "Integer",
"results": "Array[Object]"
}
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page |
Integer | Page number (default: 1) |
page_size |
Integer | Items per page (default: 50, max: 200) |
all |
String | Set to true to bypass pagination |
These endpoints allow a teacher to manage their own assistants.
Description: List all assistants belonging to the authenticated teacher.
Authentication: Teacher
Success Response — 200 OK:
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": [
{
"id": "Integer",
"user_id": "Integer",
"username": "String",
"name": "String",
"phone": "String",
"gmail": "String",
"gender": "String (male|female) | null",
"profile_picture": "String (URL) | null",
"is_active": "Boolean",
"created_at": "DateTime (ISO 8601)",
"teacher": "Integer — Teacher ID"
}
]
}
Description: Create a new assistant for the authenticated teacher.
Authentication: Teacher
Content-Type: multipart/form-data
Request Body:
{
"username": "String (Required)",
"password": "String (Required) — Minimum 8 characters",
"name": "String (Required)",
"phone": "String (Required)",
"gmail": "String (Required) — Must be globally unique",
"gender": "String (Optional) — male|female",
"profile_picture": "File (Optional) — image/jpeg|image/png|image/webp"
}
Authentication: Teacher (must own the assistant)
Authentication: Teacher (must own the assistant)
Authentication: Teacher (must own the assistant)
Success Response — 204 No Content
Description: List courses taught by the authenticated teacher.
Authentication: Teacher
Success Response — 200 OK: Array of courses.
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": [
{
"id": "Integer",
"name": "String",
"teacher": "Integer",
"teacher_name": "String",
"grade": "Integer",
"grade_name": "String",
"subject": "Integer",
"subject_name": "String",
"description": "String | null",
"cover_picture": "String (URL) | null",
"is_active": "Boolean",
"topic_count": "Integer",
"created_at": "DateTime (ISO 8601)"
}
]
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
403 |
Not a teacher | {"detail": "You do not have permission..."} |
404 |
Teacher profile not found | {"detail": "Teacher profile not found."} |
Description: List all topics.
Authentication: Any authenticated user
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
course |
Integer | Filter by course ID |
is_active |
Boolean | Filter by active status |
search |
String | Search by name, description, or course name |
ordering |
String | order, created_at, name |
Success Response — 200 OK:
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": [
{
"id": "Integer",
"course": "Integer",
"course_name": "String",
"name": "String",
"description": "String | null",
"order": "Integer",
"is_active": "Boolean",
"lecture_count": "Integer",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
]
}
Authentication: Teacher
Content-Type: application/json
Request Body:
{
"course": "Integer (Required) — Course ID",
"name": "String (Required)",
"description": "String (Optional)",
"order": "Integer (Optional) — Default: 0",
"is_active": "Boolean (Optional) — Default: true"
}
Authentication: Any authenticated user
Success Response — 200 OK: Single topic with nested lectures.
{
"id": "Integer",
"course": "Integer",
"course_name": "String",
"name": "String",
"description": "String | null",
"order": "Integer",
"is_active": "Boolean",
"lectures": [
{
"id": "Integer",
"topic": "Integer",
"topic_name": "String",
"course_name": "String",
"teacher_name": "String",
"name": "String",
"description": "String | null",
"price": "Decimal",
"discount": "Decimal",
"final_price": "Decimal",
"formatted_price": "String",
"available_days": "Integer",
"is_visible": "Boolean",
"picture": "String (URL) | null",
"order": "Integer",
"videos": "Array[Object]",
"video_count": "Integer",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
],
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
Authentication: Teacher
Authentication: Teacher
Description: Get all lectures for a specific topic.
Authentication: Any authenticated user
Success Response — 200 OK: Array of lectures.
[
{
"id": "Integer",
"topic": "Integer",
"topic_name": "String",
"name": "String",
"description": "String | null",
"price": "Decimal",
"discount": "Decimal",
"final_price": "Decimal",
"formatted_price": "String",
"available_days": "Integer",
"is_visible": "Boolean",
"picture": "String (URL) | null",
"order": "Integer",
"video_count": "Integer",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
]
Business Rules:
Authentication: Any authenticated user
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
topic |
Integer | Filter by topic ID |
is_visible |
Boolean | Filter by visibility |
search |
String | Search by name, description, or topic name |
ordering |
String | order, price, created_at, name |
Success Response — 200 OK: Array of lectures.
Authentication: Teacher
Content-Type: multipart/form-data
Request Body:
{
"topic": "Integer (Required) — Topic ID",
"name": "String (Required)",
"description": "String (Optional)",
"price": "Decimal (Required)",
"discount": "Decimal (Optional) — Default: 0.00",
"available_days": "Integer (Required) — Days of access after purchase (1-365)",
"is_visible": "Boolean (Optional) — Default: true",
"picture": "File (Optional) — image/jpeg|image/png|image/webp",
"order": "Integer (Optional) — Default: 0"
}
Success Response — 201 Created: Single lecture.
Authentication: Any authenticated user
Success Response — 200 OK: Single lecture with nested videos.
{
"id": "Integer",
"topic": "Integer",
"topic_name": "String",
"course_name": "String",
"teacher_name": "String",
"name": "String",
"description": "String | null",
"price": "Decimal",
"discount": "Decimal",
"final_price": "Decimal",
"formatted_price": "String",
"available_days": "Integer",
"is_visible": "Boolean",
"picture": "String (URL) | null",
"order": "Integer",
"videos": [
{
"id": "Integer",
"lecture": "Integer",
"name": "String",
"platform": "String (youtube|bunnystream|vdocipher)",
"platform_display": "String",
"video_url": "String",
"order": "Integer",
"duration_minutes": "Integer | null",
"duration_display": "String | null",
"is_active": "Boolean",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
],
"video_count": "Integer",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
Authentication: Teacher
Authentication: Teacher
Description: Get all videos for a specific lecture.
Authentication: Any authenticated user
Success Response — 200 OK: Array of videos.
[
{
"id": "Integer",
"lecture": "Integer",
"name": "String",
"platform": "String (youtube|bunnystream|vdocipher)",
"platform_display": "String",
"video_url": "String",
"order": "Integer",
"duration_minutes": "Integer | null",
"duration_display": "String | null",
"is_active": "Boolean",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
]
Business Rules:
Authentication: Any authenticated user
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
lecture |
Integer | Filter by lecture ID |
platform |
String | Filter by platform (youtube, bunnystream, vdocipher) |
is_active |
Boolean | Filter by active status |
search |
String | Search by name or video URL |
ordering |
String | order, created_at, duration_minutes |
Success Response — 200 OK: Array of videos (same structure as lecture videos).
Authentication: Teacher
Content-Type: application/json
Request Body:
{
"lecture": "Integer (Required) — Lecture ID",
"name": "String (Required)",
"platform": "String (Required) — youtube|bunnystream|vdocipher",
"video_url": "String (Required) — Must match platform format",
"order": "Integer (Optional) — Default: 0",
"duration_minutes": "Integer (Optional)",
"is_active": "Boolean (Optional) — Default: true"
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Invalid YouTube URL | {"video_url": ["YouTube URL must contain 'youtube.com' or 'youtu.be'."]} |
400 |
Invalid Bunny Stream URL | {"video_url": ["Bunny Stream URL must contain 'bunnycdn' or 'b-cdn.net'."]} |
400 |
Invalid VDO Cipher URL | {"video_url": ["VDO Cipher URL must be a valid HTTPS URL."]} |
Authentication: Any authenticated user
Authentication: Teacher
Authentication: Teacher
Description: List pending enrollment requests for the teacher's courses.
Authentication: Teacher, Assistant
Success Response — 200 OK: Array of pending enrollments.
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": [
{
"id": "Integer",
"student": "Integer",
"student_name": "String",
"student_code": "String",
"course": "Integer",
"course_name": "String",
"grade_name": "String",
"teacher_name": "String",
"status": "String — pending",
"status_display": "String — Pending",
"enrolled_at": "DateTime (ISO 8601)",
"approved_by": "Integer | null",
"approved_by_name": "String | null",
"responded_at": "DateTime | null",
"response_note": "String | null"
}
]
}
Description: Approve a pending enrollment.
Authentication: Teacher (course owner), Assistant (teacher's assistant)
Content-Type: application/json
Request Body:
{
"response_note": "String (Optional) — Note for the student"
}
Success Response — 200 OK: Enrollment object with status: approved.
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
403 |
Not course owner | {"error": "You can only manage enrollments for your own courses"} |
400 |
Not pending | {"error": "Cannot approve enrollment with status: X"} |
404 |
Enrollment not found | {"error": "Enrollment not found"} |
Description: Reject a pending enrollment.
Authentication: Teacher (course owner), Assistant (teacher's assistant)
Content-Type: application/json
Request Body:
{
"response_note": "String (Optional) — Note for the student"
}
Success Response — 200 OK: Enrollment object with status: rejected.
Error Responses: Same as approve endpoint.
Description: Approve or reject multiple enrollments at once.
Authentication: Teacher, Assistant
Content-Type: application/json
Request Body:
{
"enrollment_ids": "Array[Integer] (Required) — Minimum 1 ID",
"action": "String (Required) — approve|reject"
}
Success Response — 200 OK:
{
"processed": "Integer",
"total_requested": "Integer",
"action": "String (approve|reject)",
"errors": [
{
"enrollment_id": "Integer",
"error": "String"
}
]
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Invalid action | {"action": ["\"X\" is not a valid choice."]} |
400 |
Empty IDs list | {"enrollment_ids": ["Ensure this field has at least 1 elements."]} |
Business Rules:
Description: Teacher/Assistant extends a student's access to a lecture by adding extra days.
Authentication: Teacher (lecture course owner), Assistant
Content-Type: application/json
Request Body:
{
"extra_days": "Integer (Required) — Minimum 1"
}
Success Response — 200 OK: Updated purchased lecture.
{
"id": "Integer",
"student": "Integer",
"lecture": "Integer",
"lecture_name": "String",
"topic_name": "String",
"course_name": "String",
"teacher_name": "String",
"purchased_at": "DateTime (ISO 8601)",
"expires_at": "DateTime (ISO 8601)",
"effective_expiry": "DateTime (ISO 8601)",
"amount_paid": "Decimal",
"extra_days": "Integer",
"is_expired": "Boolean",
"reopened_by": "Integer | null",
"reopened_by_name": "String | null",
"reopened_at": "DateTime | null",
"reopen_logs": [
{
"extra_days": "Integer",
"reopened_by": "String | null",
"reopened_at": "DateTime",
"note": "String"
}
]
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
403 |
Not course owner | {"error": "You can only extend access for lectures in your own courses"} |
400 |
Invalid extra_days | {"extra_days": ["Ensure this value is greater than or equal to 1."]} |
404 |
Purchase not found | {"detail": "Not found."} |
Business Rules:
effective_expiry is recalculated as expires_at + extra_days.Description: List questions accessible to the authenticated teacher.
Authentication: Teacher, Assistant
Query Parameters:
lesson — Filter by lesson IDquestion_type — mcq_single, mcq_multiple, writtendifficulty — easy, medium, hardis_global — true or falselesson__chapter__subject — Filter by subjectlesson__chapter__grade — Filter by gradesearch — Search in question text and explanationSuccess Response — 200 OK:
{
"count": "integer - Number of items",
"results": [
{
"id": "integer - Unique identifier",
"lesson": "integer - Lesson ID",
"lesson_name": "String - Lesson name",
"chapter_name": "String - Chapter name",
"subject_name": "String - Subject name",
"question_type": "String (mcq_single|mcq_multiple|written)",
"difficulty": "String (easy|medium|hard)",
"points": "Integer - Question point value",
"choice_count": "Integer - Number of choices",
"is_global": "Boolean - Whether question is shared with other teachers",
"created_by": "Integer - User ID of creator",
"created_at": "DateTime (ISO 8601)"
}
]
}
Description: Create a new question with optional choices.
Authentication: Teacher, Assistant, SiteOwner
Request Body:
{
"lesson": "Integer - Lesson ID",
"question_type": "String (mcq_single|mcq_multiple|written)",
"text": "String - Question text",
"image": "String (URL) | null",
"difficulty": "String (easy|medium|hard)",
"points": "Integer - Question point value",
"explanation": "String - Explanation",
"is_global": "Boolean - Whether question is shared with other teachers",
"choices": [
{"text": "String - Choice text", "image": "String (URL) | null", "is_correct": "Boolean - Whether this is correct", "order": "Integer - Display order"},
{"text": "String - Choice text", "image": "String (URL) | null", "is_correct": "Boolean - Whether this is correct", "order": "Integer - Display order"},
{"text": "String - Choice text", "image": "String (URL) | null", "is_correct": "Boolean - Whether this is correct", "order": "Integer - Display order"},
{"text": "String - Choice text", "image": "String (URL) | null", "is_correct": "Boolean - Whether this is correct", "order": "Integer - Display order"}
]
}
Notes:
choices is optional for written type questionstext or image (or both)text or image (or both)Success Response — 201 Created:
{
"id": "Integer - Unique identifier",
"lesson": "Integer - Lesson ID",
"question_type": "String (mcq_single|mcq_multiple|written)",
"text": "String - Question text",
...
}
Description: Retrieve a single question with all choices (including is_correct).
Authentication: Teacher, Assistant
Description: Update a question. Sending choices will replace all existing choices.
Authentication: Teacher, Assistant, SiteOwner
Description: Delete a question.
Authentication: Teacher, Assistant, SiteOwner
Description: Randomly select question IDs from the bank for quiz/homework creation.
Authentication: Teacher, Assistant
Request Body:
{
"lesson": "Integer - Lesson ID",
"question_type": "String (mcq_single|mcq_multiple|written)",
"difficulty": "String (easy|medium|hard)",
"count": "Integer - Number of items"
}
Success Response — 200 OK:
{
"total_available": "Integer - Total available questions",
"requested": "Integer - Number requested",
"selected": "Array[Integer] - Selected question IDs"
}
Description: List homeworks.
Authentication: Any authenticated user (teachers see their course homeworks)
Query Parameters:
lecture — Filter by lecture IDis_active — true or falseis_published — true or falseSuccess Response — 200 OK:
{
"count": "integer - Number of items",
"results": [
{
"id": "Integer - Unique identifier",
"lecture": "Integer - Lecture ID",
"lecture_name": "String - Lecture name",
"title": "String - Homework title",
"description": "String - Description",
"is_active": "Boolean - Whether this item is active",
"is_published": "Boolean - Whether this item is published",
"total_points": "Integer - Total points",
"question_count": "Integer - Number of questions",
"open_date": "DateTime (ISO 8601) | null",
"close_date": "DateTime (ISO 8601) | null",
"created_at": "DateTime (ISO 8601)"
}
]
}
Description: Create a new homework.
Authentication: Teacher, Assistant, SiteOwner
Request Body:
{
"lecture": "Integer - Lecture ID",
"title": "String - Homework title",
"description": "String - Description",
"model_answer": "String - Model answer",
"is_active": "Boolean - Whether this item is active",
"is_published": "Boolean - Whether this item is published",
"open_date": "DateTime (ISO 8601) | null",
"close_date": "DateTime (ISO 8601) | null",
"question_ids": "Array[Integer] - Question IDs"
}
Notes:
model_answer is shown to students after they submitDescription: Retrieve a homework with questions.
Authentication: Teacher, Assistant, SiteOwner (students must have purchased the lecture)
Success Response — 200 OK:
{
"id": "Integer",
"lecture": "Integer",
"lecture_name": "String",
"title": "String",
"description": "String",
"model_answer": "String",
"is_active": "Boolean",
"is_published": "Boolean",
"total_points": "Integer",
"question_count": "Integer",
"questions": [
{
"id": "Integer",
"question": "Integer - QuestionBank ID",
"order": "Integer",
"points_override": "Integer | null",
"effective_points": "Integer",
"question_text": "String",
"question_image": "String (URL) | null",
"question_type": "String (mcq_single|mcq_multiple)",
"choices": [
{
"id": "Integer",
"text": "String",
"image": "String (URL) | null",
"is_correct": "Boolean",
"order": "Integer"
}
]
}
],
"open_date": "DateTime (ISO 8601) | null",
"close_date": "DateTime (ISO 8601) | null",
"created_at": "DateTime (ISO 8601)"
}
Description: Update a homework.
Authentication: Teacher, Assistant, SiteOwner
Description: Delete a homework.
Authentication: Teacher, Assistant, SiteOwner
Description: List all submissions for a homework.
Authentication: Teacher, Assistant
Description: View a specific submission (includes model answer for the student).
Authentication: Student (own submissions), Teacher, Assistant
Description: List quizzes.
Authentication: Any authenticated user (teachers see their course quizzes)
Query Parameters:
lecture — Filter by lecture IDis_active — true or falseis_published — true or falseSuccess Response — 200 OK:
{
"count": "integer - Number of items",
"results": [
{
"id": "Integer - Unique identifier",
"lecture": "Integer - Lecture ID",
"lecture_name": "String - Lecture name",
"title": "String - Quiz title",
"description": "String - Description",
"passing_score": "Decimal - Minimum score required to pass",
"is_active": "Boolean - Whether this item is active",
"is_published": "Boolean - Whether this item is published",
"total_points": "Integer - Total points",
"question_count": "Integer - Number of questions",
"settings": {
"open_date": "DateTime (ISO 8601) | null",
"close_date": "DateTime (ISO 8601) | null",
"timer_minutes": "Integer - Time limit in minutes",
"score_visibility": "String (immediate|after_close|manual)",
"answers_visibility": "String (immediate|after_close|manual)",
"question_order": "String (fixed|random)",
"shuffle_choices": "Boolean - Whether choices are shuffled",
"allow_multiple_attempts": "Boolean - Whether multiple attempts are allowed",
"max_attempts": "Integer - Maximum number of attempts",
"attempt_scoring": "String (best|average|last)",
"show_correct_after_submission": "Boolean - Whether correct answers are shown after submission"
},
"created_at": "DateTime (ISO 8601)"
}
]
}
Description: Create a new quiz with settings and question assignments.
Authentication: Teacher, Assistant, SiteOwner
Request Body:
{
"lecture": "Integer - Lecture ID",
"title": "String - Quiz title",
"description": "String - Description",
"passing_score": "Decimal - Minimum score required to pass",
"is_active": "Boolean - Whether this item is active",
"is_published": "Boolean - Whether this item is published",
"settings": {
"timer_minutes": "Integer - Time limit in minutes",
"score_visibility": "String (immediate|after_close|manual)",
"answers_visibility": "String (immediate|after_close|manual)",
"question_order": "String (fixed|random)",
"shuffle_choices": "Boolean - Whether choices are shuffled",
"allow_multiple_attempts": "Boolean - Whether multiple attempts are allowed",
"max_attempts": "Integer - Maximum number of attempts",
"attempt_scoring": "String (best|average|last)",
"show_correct_after_submission": "Boolean - Whether correct answers are shown after submission"
},
"question_ids": "Array[Integer] - Question IDs"
}
Notes:
passing_score is optional (null = no passing threshold)question_ids are QuestionBank IDs; they will be assigned in orderscore_visibility / answers_visibility: immediate, after_close, manualDescription: Retrieve a quiz with questions and settings.
Authentication: Teacher, Assistant, SiteOwner (students must have purchased the lecture)
Success Response — 200 OK:
{
"id": "Integer",
"lecture": "Integer",
"lecture_name": "String",
"title": "String",
"description": "String",
"passing_score": "Decimal | null",
"is_active": "Boolean",
"is_published": "Boolean",
"total_points": "Integer",
"question_count": "Integer",
"settings": {
"open_date": "DateTime (ISO 8601) | null",
"close_date": "DateTime (ISO 8601) | null",
"timer_minutes": "Integer",
"score_visibility": "String (immediate|after_close|manual)",
"answers_visibility": "String (immediate|after_close|manual)",
"question_order": "String (fixed|random)",
"shuffle_choices": "Boolean",
"allow_multiple_attempts": "Boolean",
"max_attempts": "Integer | null",
"attempt_scoring": "String (best|average|last)",
"show_correct_after_submission": "Boolean"
},
"questions": [
{
"id": "Integer",
"question": "Integer - QuestionBank ID",
"order": "Integer",
"points_override": "Integer | null",
"effective_points": "Integer",
"question_text": "String",
"question_image": "String (URL) | null",
"question_type": "String (mcq_single|mcq_multiple|written)",
"choices": [
{
"id": "Integer",
"text": "String",
"image": "String (URL) | null",
"is_correct": "Boolean",
"order": "Integer"
}
]
}
],
"created_at": "DateTime (ISO 8601)"
}
Description: Update a quiz. Sending question_ids replaces all questions.
Authentication: Teacher, Assistant, SiteOwner
Description: Delete a quiz and all its submissions.
Authentication: Teacher, Assistant, SiteOwner
Description: Release scores for all submissions of a quiz (for manual visibility).
Authentication: Teacher, Assistant
Success Response — 200 OK:
{
"detail": "String - Success message"
}
Description: Release correct answers for all submissions of a quiz (for manual visibility).
Authentication: Teacher, Assistant
Description: View a specific submission.
Authentication: Student (own submissions), Teacher, Assistant
Notes:
score_visibility settinganswers_visibility settingDescription: Manually grade a written answer.
Authentication: Teacher, Assistant
Request Body:
{
"answer_id": "Integer - Answer ID",
"score_override": "Decimal - Manual score override",
"feedback": "String - Feedback text"
}
Description: Upload a new PDF study material.
Authentication: Teacher, Assistant, SiteOwner
Content-Type: multipart/form-data
Request Body:
{
"lecture": "Integer - Lecture ID",
"title": "String - Material title",
"description": "String - Description",
"file": <PDF_FILE>
}
Validation:
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
File too large | {"file": ["File too large. Size should not exceed 50 MB."]} |
400 |
Invalid file type | {"file": ["File extension 'xyz' is not allowed. Allowed extensions are: pdf."]} |
Description: List study materials.
Authentication: Any authenticated user (teachers see their course materials)
Query Parameters:
lecture — Filter by lecture IDis_active — true or falseDescription: Retrieve a single material.
Authentication: Teacher, Assistant, SiteOwner (students must have purchased the lecture)
Description: Update a material (replace PDF, change title, etc.).
Authentication: Teacher, Assistant, SiteOwner
Description: Delete a material.
Authentication: Teacher, Assistant, SiteOwner
Description: Teacher or Assistant directly adds balance to a student's course account. Creates a full audit trail.
Authentication: Teacher / Assistant
Request Body:
{
"student_id": "String (Required) — StudentProfile PK or student_code",
"course_id": "Integer (Required) — Course ID",
"amount": "String (Required) — Positive Decimal (e.g. '75.50')",
"reason": "String (Optional) — Note/reason for the addition"
}
Success Response — 200 OK:
{
"detail": "String - Success message",
"student": "String - Student name",
"student_code": "String",
"course": "String - Course name",
"amount_added": "String (Decimal) - Amount added",
"new_balance": "String (Decimal) - New balance",
"transaction_id": "Integer"
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Invalid amount | {"amount": ["Ensure this value is greater than or equal to 0.01."]} |
403 |
Not your course | {"error": "You can only add balance for your own courses"} |
403 |
Student not enrolled | {"error": "Student is not enrolled in this course"} |
404 |
Student not found | {"error": "Student not found"} |
404 |
Course not found | {"error": "Course not found"} |
Business Rules:
Decimal — no float precision errors.BalanceTransaction with balance_before and balance_after.StudentActivity log entry with type BALANCE_ADDED.Notification to the student.Description: Teacher/Assistant views balances of all students enrolled in their courses.
Authentication: Teacher / Assistant
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
course |
Integer | Filter by course ID |
student |
String | Filter by student ID or student_code |
Success Response — 200 OK:
[
{
"student_id": "Integer",
"student_name": "String",
"student_code": "String",
"course_id": "Integer",
"course_name": "String",
"balance": "String — Decimal as string",
"enrolled_at": "DateTime (ISO 8601)"
}
]
Description: Get aggregated stats for the teacher's dashboard.
Authentication: Teacher, Assistant
Success Response — 200 OK:
{
"total_courses": "Integer",
"total_students": "Integer",
"total_revenue": "Decimal",
"recent_enrollments": [
{
"id": "Integer",
"student_name": "String",
"course_name": "String",
"status": "String",
"enrolled_at": "DateTime (ISO 8601)"
}
],
"recent_purchases": [
{
"id": "Integer",
"student_name": "String",
"lecture_name": "String",
"amount_paid": "Decimal",
"purchased_at": "DateTime (ISO 8601)"
}
],
"course_breakdown": [
{
"id": "Integer",
"name": "String",
"student_count": "Integer",
"purchase_count": "Integer",
"revenue": "Decimal"
}
]
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
403 |
Not teacher/assistant | {"detail": "You do not have permission..."} |
Business Rules:
Description: List all notifications for the authenticated user.
Authentication: Any authenticated user
Query Parameters:
is_read — true or falsenotification_type — enrollment_approved, enrollment_rejected, balance_added, etc.Success Response — 200 OK:
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": [
{
"id": "Integer",
"notification_type": "String",
"notification_type_display": "String",
"title": "String",
"message": "String",
"is_read": "Boolean",
"metadata": "Object | null",
"created_at": "DateTime (ISO 8601)"
}
]
}
Description: Get count of unread notifications.
Authentication: Any authenticated user
Success Response — 200 OK:
{
"count": "Integer"
}
Description: Mark all notifications as read.
Authentication: Any authenticated user
Request Body: None
Success Response — 200 OK:
{
"message": "All notifications marked as read"
}
Description: Mark a notification as read.
Authentication: Any authenticated user
Request Body: None
Success Response — 200 OK:
{
"message": "Notification marked as read"
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
404 |
Notification not found | {"error": "Notification not found"} |