EduTrack Online - Teacher API

Version: 1.1 Date: April 29, 2026 Target Audience: Next.js Frontend Developers (Teacher Views) Backend: Django REST Framework


Authentication

The API uses JWT tokens stored in HTTP-Only cookies.

Inactive User Blocking:

Common Errors

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"}

Pagination

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

1. Assistant Management

These endpoints allow a teacher to manage their own assistants.


GET /accounts/teacher/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" } ] }

POST /accounts/teacher/assistants/

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" }

GET /accounts/teacher/assistant//

Authentication: Teacher (must own the assistant)


PUT/PATCH /accounts/teacher/assistant//

Authentication: Teacher (must own the assistant)


DELETE /accounts/teacher/assistant//

Authentication: Teacher (must own the assistant)

Success Response — 204 No Content


2. My Courses


GET /courses/my_courses/

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."}

3. Course Content Management


GET /courses/topics/

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)" } ] }

POST /courses/topics/

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" }

GET /courses/topics//

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)" }

PUT/PATCH /courses/topics//

Authentication: Teacher


DELETE /courses/topics//

Authentication: Teacher


GET /courses/topics//lectures/

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:


4. Lectures


GET /courses/lectures/

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.


POST /courses/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.


GET /courses/lectures//

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)" }

PUT/PATCH /courses/lectures//

Authentication: Teacher


DELETE /courses/lectures//

Authentication: Teacher


GET /courses/lectures//videos/

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:


5. Videos


GET /courses/videos/

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).


POST /courses/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."]}

GET /courses/videos//

Authentication: Any authenticated user


PUT/PATCH /courses/videos//

Authentication: Teacher


DELETE /courses/videos//

Authentication: Teacher


6. Enrollments


GET /courses/enrollments/pending/

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" } ] }

POST /courses/enrollments//approve/

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"}

POST /courses/enrollments//reject/

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.


POST /courses/enrollments/bulk-action/

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:


7. Purchases


PATCH /courses/purchases//reopen/

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:


8. Question Bank


GET /question-bank/questions/

Description: List questions accessible to the authenticated teacher.

Authentication: Teacher, Assistant

Query Parameters:

Success 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)" } ] }

POST /question-bank/questions/

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:

Success Response — 201 Created:

{ "id": "Integer - Unique identifier", "lesson": "Integer - Lesson ID", "question_type": "String (mcq_single|mcq_multiple|written)", "text": "String - Question text", ... }

GET /question-bank/questions/{id}/

Description: Retrieve a single question with all choices (including is_correct).

Authentication: Teacher, Assistant


PUT /question-bank/questions/{id}/

Description: Update a question. Sending choices will replace all existing choices.

Authentication: Teacher, Assistant, SiteOwner


DELETE /question-bank/questions/{id}/

Description: Delete a question.

Authentication: Teacher, Assistant, SiteOwner


POST /question-bank/questions/random-select/

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" }

9. Homeworks


GET /learning/homeworks/

Description: List homeworks.

Authentication: Any authenticated user (teachers see their course homeworks)

Query Parameters:

Success 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)" } ] }

POST /learning/homeworks/

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:


GET /learning/homeworks/{id}/

Description: 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)" }

PUT /learning/homeworks/{id}/

Description: Update a homework.

Authentication: Teacher, Assistant, SiteOwner


DELETE /learning/homeworks/{id}/

Description: Delete a homework.

Authentication: Teacher, Assistant, SiteOwner


GET /learning/homeworks/{id}/submissions/

Description: List all submissions for a homework.

Authentication: Teacher, Assistant


GET /learning/homework-submissions/{id}/

Description: View a specific submission (includes model answer for the student).

Authentication: Student (own submissions), Teacher, Assistant


10. Quizzes


GET /learning/quizzes/

Description: List quizzes.

Authentication: Any authenticated user (teachers see their course quizzes)

Query Parameters:

Success 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)" } ] }

POST /learning/quizzes/

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:


GET /learning/quizzes/{id}/

Description: 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)" }

PUT /learning/quizzes/{id}/

Description: Update a quiz. Sending question_ids replaces all questions.

Authentication: Teacher, Assistant, SiteOwner


DELETE /learning/quizzes/{id}/

Description: Delete a quiz and all its submissions.

Authentication: Teacher, Assistant, SiteOwner


POST /learning/quizzes/{id}/release-scores/

Description: Release scores for all submissions of a quiz (for manual visibility).

Authentication: Teacher, Assistant

Success Response — 200 OK:

{ "detail": "String - Success message" }

POST /learning/quizzes/{id}/release-answers/

Description: Release correct answers for all submissions of a quiz (for manual visibility).

Authentication: Teacher, Assistant


GET /learning/quiz-submissions/{id}/

Description: View a specific submission.

Authentication: Student (own submissions), Teacher, Assistant

Notes:


POST /learning/quiz-submissions/{id}/grade-written/

Description: 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" }

11. Study Materials


POST /materials/

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."]}

GET /materials/

Description: List study materials.

Authentication: Any authenticated user (teachers see their course materials)

Query Parameters:


GET /materials/{id}/

Description: Retrieve a single material.

Authentication: Teacher, Assistant, SiteOwner (students must have purchased the lecture)


PUT /materials/{id}/

Description: Update a material (replace PDF, change title, etc.).

Authentication: Teacher, Assistant, SiteOwner


DELETE /materials/{id}/

Description: Delete a material.

Authentication: Teacher, Assistant, SiteOwner


12. Balance


POST /payments/balance/add/

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:


GET /payments/balance/students/

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)" } ]

13. Dashboard


GET /courses/dashboard/teacher/

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:


14. Notifications


GET /notifications/

Description: List all notifications for the authenticated user.

Authentication: Any authenticated user

Query Parameters:

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)" } ] }

GET /notifications/unread-count/

Description: Get count of unread notifications.

Authentication: Any authenticated user

Success Response — 200 OK:

{ "count": "Integer" }

POST /notifications/mark-all-read/

Description: Mark all notifications as read.

Authentication: Any authenticated user

Request Body: None

Success Response — 200 OK:

{ "message": "All notifications marked as read" }

POST /notifications//mark-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"}