Version: 1.1 Date: April 29, 2026 Target Audience: Next.js Frontend Developers (Student Features) Backend: Django REST Framework
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)"
}
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 |
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:
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:
grade, school_type (via subject), division (via subject), and educational_state.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"} |
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:
Description: Student views their own enrollments.
Authentication: Student
Success Response — 200 OK: Array of enrollments.
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"
}
]
}
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"} |
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
purchaseobject is only included ifis_purchasedistrue.
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"} |
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:
final_price is deducted from the student's course balance.expires_at is calculated as purchased_at + available_days.Description: Student views their own purchased lectures.
Authentication: Student
Success Response — 200 OK: Array of purchased lectures.
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"
}
]
}
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:
is_completed is auto-set to true when progress_percentage >= 90%.watch_count increments on each update.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."} |
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)"
}
]
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:
XXXX-XXXX-XXXX-XXXX (16 random chars, cryptographically secure).BalanceTransaction ledger entry.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)"
}
]
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)"
}
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"
}
]
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"}
]
}
]
}
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:
choice_ids for MCQ types (array of choice IDs)written_answer for written type questionsSuccess 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"
}
Description: List all student submissions for a quiz.
Authentication: Teacher, Assistant
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)"
}
]
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"} |
Documentation generated by OpenCode AI Agent Project: EduTrack Online Backend Last Updated: April 29, 2026