EduTrack Online - Student API

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


Table of Contents

  1. Profile Management
  2. Change Password
  3. Course Discovery
  4. Enrollments
  5. Course Lectures
  6. Purchases
  7. Video Watch Progress
  8. Balance
  9. Homeworks
  10. Quizzes
  11. Study Materials
  12. Notifications

1. Profile Management


GET /accounts/profile/me/

Description: Get the current student's own profile.

Authentication: Student

Success Response — 200 OK:

{ "id": "Integer", "user_id": "Integer", "username": "String", "email": "String", "student_code": "String", "name_ar": "String", "name_en": "String", "full_name": "String", "phone_number": "String", "father_number": "String | null", "mother_number": "String | null", "father_job": "String", "educational_state": "school", "school_type": "Integer | null", "school_type_name": "String | null", "grade": "Integer | null", "grade_name": "String | null", "division": "Integer | null", "division_name": "String | null", "school_name": "String | null", "national_id": "String", "birth_date": "Date (YYYY-MM-DD)", "gender": "String (male|female)", "gmail": "String", "facebook_link": "String | null", "governorate": "Integer", "governorate_name": "String | null", "area": "Integer", "area_name": "String | null", "status": "String", "is_active": "Boolean", "can_access_course": "Boolean", "created_at": "DateTime (ISO 8601)", "updated_at": "DateTime (ISO 8601)" }

PATCH /accounts/profile/me/

Description: Update the current student's own profile.

Authentication: Student

Content-Type: multipart/form-data or application/json

Request Body: All fields from the profile are optional for PATCH. Same validation rules as registration apply.

Success Response — 200 OK: Same structure as GET.

Error Responses:

Status Condition Response Body
400 Email exists (other user) {"gmail": ["This email is already associated with an account."]}
400 Area/governorate mismatch {"area": ["The selected area does not belong..."]}
400 Missing required fields for state Same as registration validation errors

2. Change Password


POST /accounts/change-password/

Description: Allows an authenticated user to change their password. Requires the current password for verification. Invalidates all refresh tokens on success.

Authentication: Any authenticated user

Content-Type: application/json

Request Body:

{ "old_password": "String (Required) — Current password", "new_password": "String (Required) — Minimum 8 characters", "new_password_confirm": "String (Required) — Must match new_password" }

Success Response — 200 OK:

{ "message": "Password changed successfully. Please log in again." }

Error Responses:

Status Condition Response Body
400 Missing fields {"error": "All password fields are required"}
400 Passwords don't match {"error": "New passwords do not match"}
400 Password too short {"error": "Password must be at least 8 characters"}
400 Wrong old password {"error": "Current password is incorrect"}
401 Not authenticated {"detail": "Authentication credentials were not provided."}

Business Rules:


3. Course Discovery


GET /courses/by-subject/<subject_id>/

Description: List courses for a specific subject. Auto-filtered by the authenticated student's grade, division, and school type. Only active courses are returned.

Authentication: Any authenticated user

Query Parameters:

Parameter Type Description
teacher Integer Filter by teacher ID
search String Search by name, description, or teacher name
ordering String created_at, name, -created_at (default: -created_at)

Success Response — 200 OK:

{ "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
401 Not authenticated {"detail": "Authentication credentials were not provided."}

Business Rules:


GET /courses//preview/

Description: Preview a course before enrolling. Shows topics and lectures with prices, but NO video URLs. Available to any authenticated user.

Authentication: Any authenticated user

Success Response — 200 OK:

{ "id": "Integer", "name": "String", "description": "String | null", "cover_picture": "String (URL) | null", "teacher": { "id": "Integer", "name": "String", "profile_picture": "String (URL) | null" }, "grade": { "id": "Integer", "name": "String" }, "subject": { "id": "Integer", "name": "String" }, "topic_count": "Integer", "total_lectures": "Integer", "topics": [ { "id": "Integer", "name": "String", "description": "String | null", "order": "Integer", "lecture_count": "Integer", "lectures": [ { "id": "Integer", "name": "String", "description": "String | null", "price": "String — Decimal as string", "final_price": "String — Decimal as string", "discount": "String — Decimal as string", "available_days": "Integer", "order": "Integer", "video_count": "Integer" } ] } ], "created_at": "DateTime (ISO 8601)" }

Error Responses:

Status Condition Response Body
401 Not authenticated {"detail": "Authentication credentials were not provided."}
404 Course not found {"error": "Course not found"}

4. Enrollments


POST /courses/enrollments/enroll/

Description: Student requests enrollment in a course. Creates a PENDING enrollment.

Authentication: Student

Content-Type: application/json

Request Body:

{ "course": "Integer (Required) — Course ID" }

Success Response — 201 Created:

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

Error Responses:

Status Condition Response Body
400 No student profile {"detail": "Student profile not found."}
400 Already enrolled {"detail": "You are already enrolled or have a pending request for this course."}
400 Grade mismatch {"detail": "String"}

Business Rules:


GET /courses/enrollments/my/

Description: Student views their own enrollments.

Authentication: Student

Success Response — 200 OK: Array of enrollments.


GET /courses/enrolled/

Description: Get all courses the student is enrolled in (approved status only).

Authentication: Student

Success Response — 200 OK:

{ "count": "Integer", "next": "String (URL) | null", "previous": "String (URL) | null", "results": [ { "id": "Integer", "name": "String", "teacher": { "id": "Integer", "name": "String" }, "grade": { "id": "Integer", "name": "String" }, "subject": { "id": "Integer", "name": "String" }, "description": "String | null", "cover_picture": "String (URL) | null", "enrolled_at": "DateTime (ISO 8601)", "status": "String — approved" } ] }

POST /courses/enrollments//cancel/

Description: Student cancels their own pending enrollment. The enrollment is deleted.

Authentication: Student

Success Response — 200 OK:

{ "message": "Enrollment cancelled successfully" }

Error Responses:

Status Condition Response Body
403 Not own enrollment {"error": "You can only cancel your own enrollments"}
400 Not pending {"error": "Cannot cancel enrollment with status: X"}
404 Enrollment not found {"error": "Enrollment not found"}

5. Course Lectures


GET /courses//lectures/

Description: Get lectures for a course the student is enrolled in. Shows purchase status for each lecture.

Authentication: Student (must be approved enrolled)

Success Response — 200 OK:

{ "course": { "id": "Integer", "name": "String" }, "topics": [ { "id": "Integer", "name": "String", "order": "Integer", "lectures": [ { "id": "Integer", "name": "String", "description": "String | null", "price": "String — Decimal as string", "final_price": "String — Decimal as string", "discount": "String — Decimal as string", "available_days": "Integer", "order": "Integer", "video_count": "Integer", "is_purchased": "Boolean", "purchase": { "id": "Integer", "purchased_at": "DateTime (ISO 8601)", "expires_at": "DateTime (ISO 8601)", "effective_expiry": "DateTime (ISO 8601)", "is_expired": "Boolean", "extra_days": "Integer", "amount_paid": "String — Decimal as string" } } ] } ] }

Note: The purchase object is only included if is_purchased is true.

Error Responses:

Status Condition Response Body
400 No student profile {"error": "Student profile not found"}
403 Not enrolled {"error": "You are not enrolled in this course"}
404 Course not found {"error": "Course not found"}

6. Purchases


POST /courses/purchases/buy/

Description: Student purchases a lecture. Deducts the lecture's final price from the student's course balance.

Authentication: Student

Content-Type: application/json

Request Body:

{ "lecture": "Integer (Required) — Lecture ID" }

Success Response — 201 Created:

{ "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
400 No student profile {"detail": "Student profile not found."}
403 Not enrolled/approved {"detail": "You must be enrolled and approved in this course before purchasing lectures."}
400 Already purchased {"detail": "You have already purchased this lecture."}
400 Insufficient balance {"detail": "Insufficient balance. You need X more."}

Business Rules:


GET /courses/purchases/my/

Description: Student views their own purchased lectures.

Authentication: Student

Success Response — 200 OK: Array of purchased lectures.


7. Video Watch Progress


GET /courses/progress/

Description: List the student's video watch progress.

Authentication: Student

Success Response — 200 OK:

{ "count": "Integer", "next": "String (URL) | null", "previous": "String (URL) | null", "results": [ { "id": "Integer", "student": "Integer", "student_name": "String", "student_code": "String", "video": "Integer", "video_name": "String", "lecture_name": "String", "course_name": "String", "progress_seconds": "Integer", "duration_seconds": "Integer | null", "progress_percentage": "Decimal", "is_completed": "Boolean", "watch_count": "Integer", "last_watched_at": "DateTime (ISO 8601)", "platform_data": "Object | null" } ] }

POST /courses/progress/update/

Description: Update watch progress for a video.

Authentication: Student

Content-Type: application/json

Request Body:

{ "video": "Integer (Required) — Video ID", "progress_seconds": "Integer (Required) — Total seconds watched", "duration_seconds": "Integer (Optional) — Total video duration", "platform_data": "Object (Optional) — Platform-specific data" }

Success Response — 200 OK:

{ "id": "Integer", "student": "Integer", "video": "Integer", "progress_seconds": "Integer", "duration_seconds": "Integer | null", "progress_percentage": "Decimal", "is_completed": "Boolean", "watch_count": "Integer", "last_watched_at": "DateTime (ISO 8601)", "platform_data": "Object | null" }

Error Responses:

Status Condition Response Body
400 Missing video {"video": ["This field is required."]}
403 Lecture not purchased {"detail": "You must purchase this lecture to track progress."}

Business Rules:


GET /courses/progress/<video_id>/

Description: Get progress for a specific video.

Authentication: Student

Success Response — 200 OK: Single progress object.

Error Responses:

Status Condition Response Body
404 Progress not found {"detail": "Not found."}

8. Balance


GET /payments/balance/my/

Description: Get all course balances for the authenticated student. Shows available balance per course.

Authentication: Student

Success Response — 200 OK:

[ { "id": "Integer", "student": "Integer", "student_name": "String", "student_code": "String", "course": "Integer", "course_name": "String", "teacher_name": "String", "balance": "String — Decimal as string (e.g. '150.00')", "updated_at": "DateTime (ISO 8601)" } ]

POST /payments/codes/redeem/

Description: Student redeems a physical voucher code to add balance to a specific course. Codes are course-specific and can only be used once.

Authentication: Student (must be approved enrolled in the course)

Request Body:

{ "code": "String (Required) — Recharge code (e.g. 'X7K9-M2P4-QR1W-L5D8')", "course": "Integer (Required) — Course ID" }

Success Response — 200 OK:

{ "detail": "String - Success message", "new_balance": "String (Decimal) - New balance", "transaction_id": "Integer" }

Error Responses:

Status Condition Response Body
400 Code already used {"detail": "This code has already been used."}
400 Code expired {"detail": "This code has expired."}
400 Wrong course {"detail": "This code cannot be used for this course."}
403 Not enrolled {"detail": "You must be enrolled and approved..."}
404 Invalid code {"detail": "Invalid code."}

Business Rules:


9. Homeworks


GET /learning/homeworks/my/

Description: Get all homework submissions for the logged-in student.

Authentication: Student

Success Response — 200 OK:

[ { "id": "integer - Unique identifier", "homework_id": "integer - Homework ID", "homework_title": "String - Homework title", "lecture_name": "String - Lecture name", "score": "String (Decimal) - Student score", "status": "String (submitted|graded)", "submitted_at": "DateTime (ISO 8601)" } ]

POST /learning/homeworks//submit/

Description: Submit homework answers. Auto-corrected immediately.

Authentication: Student

Access Control:

Request Body:

{ "answers": [ { "homework_question_id": "integer - Homework question ID", "choice_id": "integer - Selected choice ID" }, { "homework_question_id": "integer - Homework question ID", "choice_id": "integer - Selected choice ID" } ] }

Success Response — 200 OK:

{ "detail": "Homework submitted and auto-graded.", "submission_id": "integer - Submission ID", "score": "String (Decimal) - Student score", "status": "String (submitted|graded)" }

10. Quizzes


GET /learning/quizzes/my/

Description: Get all quiz submissions for the logged-in student.

Authentication: Student

Success Response — 200 OK:

[ { "id": "integer - Unique identifier", "quiz_id": "integer - Quiz ID", "quiz_title": "String - Quiz title", "lecture_name": "String - Lecture name", "attempt_number": "integer - Attempt number", "score": "String (Decimal) - Student score", "passed": "boolean - Whether the attempt passed", "submitted_at": "DateTime (ISO 8601)", "score_visible": "boolean - Whether score is visible", "answers_visible": "boolean - Whether answers are visible" } ]

POST /learning/quizzes//start/

Description: Start a quiz attempt. Creates a QuizSubmission and returns questions in the student's randomized order (if enabled).

Authentication: Student

Access Control:

Success Response — 200 OK:

{ "submission_id": "integer - Submission ID", "attempt_number": "integer - Attempt number", "timer_minutes": "integer - Time limit in minutes", "started_at": "DateTime (ISO 8601)", "questions": [ { "answer_id": "integer - Answer ID", "quiz_question_id": "integer - Quiz question ID", "question_text": "String - Question text", "question_image": "String (URL) | null", "question_type": "String (mcq_single|mcq_multiple|written)", "points": "integer - Point value", "choices": [ {"id": "integer - ...", "text": "String - Choice text", "image": "String (URL) | null", "order": "Integer - Display order"}, {"id": "integer - ...", "text": "String - Choice text", "image": "String (URL) | null", "order": "Integer - Display order"} ] } ] }

POST /learning/quizzes//submit/

Description: Submit quiz answers.

Authentication: Student

Request Body:

{ "answers": [ { "quiz_question_id": "integer - Quiz question ID", "choice_ids": "Array[Integer] - Selected choice IDs" }, { "quiz_question_id": "integer - Quiz question ID", "choice_ids": "Array[Integer] - Selected choice IDs" }, { "quiz_question_id": "integer - Quiz question ID", "written_answer": "String - Written answer" } ] }

Notes:

Success Response — 200 OK:

{ "detail": "Quiz submitted successfully.", "submission_id": "integer - Submission ID", "score": "String (Decimal) - Student score", "passed": "boolean - Whether the attempt passed", "score_visible": "boolean - Whether score is visible", "answers_visible": "boolean - Whether answers are visible" }

GET /learning/quizzes//results/

Description: List all student submissions for a quiz.

Authentication: Teacher, Assistant


11. Study Materials


GET /materials/my/

Description: Get all study materials for lectures the student has purchased.

Authentication: Student

Success Response — 200 OK:

[ { "id": "integer - Unique identifier", "lecture": "integer - Lecture ID", "lecture_name": "String - Lecture name", "title": "String - Material title", "file_url": "String (URL) - File URL", "is_active": "boolean - Whether this item is active", "created_at": "DateTime (ISO 8601)" } ]

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

Documentation generated by OpenCode AI Agent Project: EduTrack Online Backend Last Updated: April 29, 2026