Version: 1.1 Date: April 29, 2026 Target Audience: SiteOwner Frontend Backend: Django REST Framework
Description: List all teachers with full details.
Authentication: SiteOwner
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
is_active |
Boolean | Filter by active status |
subject |
Integer | Filter by subject ID |
grades |
Integer | Filter by grade ID |
search |
String | Search by name |
all |
String | Set to true to bypass pagination |
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",
"secondary_phone": "String | null",
"gmail": "String",
"gender": "String (male|female)",
"profile_picture": "String (URL) | null",
"biography": "String | null",
"facebook": "String | null",
"subject_detail": {
"id": "Integer",
"name": "String",
"grades": "Array[Integer]",
"grades_detail": [
{
"id": "Integer",
"name": "String"
}
],
"divisions": "Array[Integer]",
"divisions_detail": [
{
"id": "Integer",
"name": "String",
"grades": [
{
"id": "Integer",
"name": "String"
}
],
"school_types": [
{
"id": "Integer",
"name": "String"
}
]
}
],
"school_types": "Array[Integer]",
"school_types_detail": [
{
"id": "Integer",
"name": "String"
}
],
"grade_ids": "Array[Integer]",
"division_ids": "Array[Integer]",
"school_type_ids": "Array[Integer]"
},
"grades_detail": [
{
"id": "Integer",
"name": "String"
}
],
"video_security_detail": {
"id": "Integer",
"name": "String",
"price_per_student": "Decimal"
},
"is_active": "Boolean",
"created_at": "DateTime (ISO 8601)",
"subject_id": "Integer | null",
"grade_ids": "Array[Integer]",
"video_security_id": "Integer | null"
}
]
}
Authentication: SiteOwner
Success Response — 200 OK: Same structure as list item.
Description: Create a new teacher user and profile atomically. Supports profile picture upload via multipart/form-data.
Authentication: SiteOwner
Content-Type: multipart/form-data
Request Body:
{
"username": "String (Required)",
"password": "String (Required) — Minimum 8 characters",
"name": "String (Required)",
"phone": "String (Required) — Egyptian format: 010/011/012/015 + 8 digits",
"secondary_phone": "String (Optional) — Egyptian format",
"gmail": "String (Required) — Must be globally unique",
"gender": "String (Required) — male|female",
"profile_picture": "File (Optional) — image/jpeg|image/png|image/webp",
"biography": "String (Optional)",
"facebook": "String (Optional)",
"is_active": "Boolean (Optional) — Default: true",
"subject": "Integer (Required) — Subject ID",
"grades": "Array[Integer] (Required) — Grade IDs",
"video_security": "Integer (Optional) — VideoSecurity ID"
}
Success Response — 201 Created: Same structure as GET list item.
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Username exists | {"username": ["Username already exists."]} |
400 |
Email exists | {"gmail": ["This email is already associated with an account."]} |
400 |
Invalid phone | {"phone": ["Phone number must be exactly 11 digits..."]} |
400 |
Weak password | {"password": ["Password validation error"]} |
403 |
Not authorized | {"detail": "You do not have permission..."} |
Business Rules:
subject is a single Subject ID.Authentication: SiteOwner
Content-Type: multipart/form-data
Request Body: Same as POST (all fields optional for PATCH).
Success Response — 200 OK: Same structure as list item.
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Email exists (other user) | {"gmail": ["This email is already associated with an account."]} |
403 |
Not authorized | {"detail": "You do not have permission..."} |
404 |
Teacher not found | {"detail": "Not found."} |
Description: List all students.
Authentication: SiteOwner
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status |
String | Filter by status |
grade |
Integer | Filter by grade ID |
school_type |
Integer | Filter by school type ID |
search |
String | Search by name, student code, or national ID |
all |
String | Set to true to bypass pagination |
Success Response — 200 OK: Array of student profiles.
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": [
{
"id": "Integer",
"user_id": "Integer",
"username": "String",
"student_code": "String",
"name_ar": "String",
"name_en": "String",
"phone_number": "String",
"father_number": "String | null",
"mother_number": "String | null",
"father_job": "String",
"educational_state": "String (school)",
"school_type": "Integer | null",
"grade": "Integer | null",
"division": "Integer | 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",
"area": "Integer",
"status": "String",
"is_active": "Boolean",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
]
}
Authentication: SiteOwner
Success Response — 200 OK: Single student profile.
Description: Update a student's status and active flag.
Authentication: SiteOwner
Content-Type: application/json
Request Body:
{
"status": "String (Optional) — verified|pending|declined|suspended_temporary|suspended_permanent",
"is_active": "Boolean (Optional)"
}
Success Response — 200 OK:
{
"status": "String",
"is_active": "Boolean"
}
Business Rules:
declined or suspended_permanent will also set is_active to false.verified will also set is_active to true.Description: Create a new course.
Authentication: SiteOwner only
Content-Type: multipart/form-data
Request Body:
{
"name": "String (Required)",
"teacher": "Integer (Required) — Teacher ID",
"grade": "Integer (Required) — Grade ID",
"description": "String (Optional)",
"cover_picture": "File (Optional) — image/jpeg|image/png|image/webp",
"is_active": "Boolean (Optional) — Default: true"
}
Success Response — 201 Created:
{
"id": "Integer",
"name": "String",
"teacher": "Integer",
"teacher_id": "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",
"topics": "Array[Object]",
"topic_count": "Integer",
"total_lectures": "Integer",
"total_videos": "Integer",
"created_at": "DateTime (ISO 8601)",
"updated_at": "DateTime (ISO 8601)"
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Teacher doesn't teach grade | {"grade": ["Teacher X does not teach grade Y."]} |
400 |
Duplicate course | {"non_field_errors": ["A course with this Teacher, Grade, and Name already exists."]} |
403 |
Not site owner | {"detail": "You do not have permission..."} |
Business Rules:
teacher.subject — it is NOT sent in the request.Authentication: SiteOwner only
Content-Type: multipart/form-data
Request Body: Same as POST (all fields optional for PATCH).
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Changing teacher | {"teacher": ["Cannot change the teacher of an existing course."]} |
400 |
Changing grade | {"grade": ["Cannot change the grade of an existing course."]} |
Business Rules:
Authentication: SiteOwner only
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Students enrolled | {"detail": "Cannot delete a course that has students enrolled in it."} |
Business Rules:
Description: SiteOwner can view all assistants across all teachers.
Authentication: SiteOwner
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
teacher |
Integer | Filter by teacher ID |
search |
String | Search by name, phone, or gmail |
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)",
"is_active": "Boolean",
"created_at": "DateTime (ISO 8601)",
"teacher": "Integer — Teacher ID",
"teacher_name": "String - Teacher's name"
}
]
}
Description: Generate new recharge codes. Codes are course-specific and can only be redeemed for the course they are generated for.
Authentication: SiteOwner
Content-Type: application/json
Request Body:
{
"course": "Integer (Required) — Course ID",
"value": "Decimal (Required) — Amount in EGP (minimum 1.00)",
"expires_at": "DateTime (Optional) — ISO 8601, code cannot be redeemed after this date"
}
Success Response — 201 Created:
{
"id": "Integer",
"course": "Integer",
"course_name": "String",
"teacher_name": "String",
"code": "String — Auto-generated (e.g. X7K9-M2P4-QR1W-L5D8)",
"value": "Decimal",
"is_used": "boolean - Whether this code has been redeemed",
"used_by": null,
"used_by_name": null,
"used_at": null,
"created_at": "DateTime (ISO 8601)",
"expires_at": "DateTime (ISO 8601) | null",
"created_by": "Integer",
"created_by_username": "String"
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
400 |
Invalid course | {"course": ["Invalid pk \"X\" - object does not exist."]} |
400 |
Invalid value | {"value": ["Ensure this value is greater than or equal to 1."]} |
403 |
Not site owner | {"detail": "You do not have permission..."} |
Business Rules:
XXXX-XXXX-XXXX-XXXX (16 random characters, cryptographically secure).Description: List all recharge codes. Supports filtering and ordering.
Authentication: SiteOwner
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
course |
Integer | Filter by course ID |
is_used |
Boolean | Filter by usage status |
Success Response — 200 OK:
{
"count": "Integer",
"next": "String (URL) | null",
"previous": "String (URL) | null",
"results": [
{
"id": "Integer",
"course": "Integer",
"course_name": "String",
"teacher_name": "String",
"code": "String",
"value": "Decimal",
"is_used": "Boolean",
"used_by": "Integer | null",
"used_by_name": "String | null",
"used_at": "DateTime (ISO 8601) | null",
"created_at": "DateTime (ISO 8601)",
"expires_at": "DateTime (ISO 8601) | null",
"created_by": "Integer",
"created_by_username": "String"
}
]
}
Description: SiteOwner revenue overview across all courses.
Authentication: SiteOwner
Success Response — 200 OK:
{
"summary": {
"total_revenue": "String — Decimal as string",
"total_purchases": "Integer",
"total_students": "Integer",
"total_courses": "Integer"
},
"courses": [
{
"id": "Integer",
"name": "String",
"is_active": "Boolean",
"enrolled_count": "Integer",
"purchase_count": "Integer",
"revenue": "String — Decimal as string"
}
]
}
Description: Deep revenue dive for a specific course. Includes per-topic and per-lecture breakdowns, plus student spending distribution.
Authentication: SiteOwner
Success Response — 200 OK:
{
"course_id": "Integer",
"course_name": "String",
"total_revenue": "String — Decimal as string",
"total_purchases": "Integer",
"topics": [
{
"topic_id": "Integer",
"topic_name": "String",
"lecture_count": "Integer",
"purchase_count": "Integer",
"revenue": "String — Decimal as string"
}
],
"lectures": [
{
"lecture_id": "Integer",
"lecture_name": "String",
"topic_id": "Integer",
"topic_name": "String",
"price": "String",
"final_price": "String",
"purchase_count": "Integer",
"revenue": "String — Decimal as string"
}
],
"student_spending": [
{
"student_id": "Integer",
"student_name": "String",
"student_code": "String",
"total_spent": "String — Decimal as string",
"lectures_purchased": "Integer"
}
]
}
Description: Revenue details for a specific lecture, including purchase timeline and average time-to-purchase.
Authentication: SiteOwner
Success Response — 200 OK:
{
"lecture_id": "Integer",
"lecture_name": "String",
"course_name": "String",
"price": "String",
"final_price": "String",
"total_revenue": "String — Decimal as string",
"buyer_count": "Integer",
"purchase_count": "Integer",
"average_days_to_purchase": "Float",
"purchase_timeline": [
{
"student_name": "String",
"student_code": "String",
"amount_paid": "String — Decimal as string",
"purchased_at": "DateTime (ISO 8601)"
}
]
}
Description: Per-student revenue breakdown across all courses.
Authentication: SiteOwner
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
course |
Integer | Filter by course ID |
Success Response — 200 OK:
[
{
"student_id": "Integer",
"student_name": "String",
"student_code": "String",
"courses_enrolled": "Integer",
"total_spent": "String — Decimal as string",
"lectures_purchased": "Integer",
"last_purchase_at": "DateTime (ISO 8601) | null"
}
]
Description: Revenue trends over time. Defaults to the past 30 days. Supports custom date ranges.
Authentication: SiteOwner
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
start_date |
String (YYYY-MM-DD) | Start of date range |
end_date |
String (YYYY-MM-DD) | End of date range |
Success Response — 200 OK:
{
"start_date": "String (YYYY-MM-DD) - Start of date range",
"end_date": "String (YYYY-MM-DD) - End of date range",
"total_revenue": "String — Decimal as string",
"total_purchases": "Integer",
"daily_trends": [
{
"date": "String (YYYY-MM-DD)",
"revenue": "String — Decimal as string",
"purchase_count": "Integer"
}
]
}
Business Rules:
end_date provided, start_date = end_date - 30 days.start_date provided, end_date = today.Description: Returns comprehensive dashboard statistics for site owners. Aggregates system-wide metrics including user counts, time-based metrics, and detailed breakdowns.
Authentication: SiteOwner
Success Response — 200 OK:
{
"total_students": "integer - Total number of student accounts",
"total_teachers": "integer - Total number of teacher accounts",
"total_assistants": "integer - Total number of assistant accounts",
"new_users_this_month": "integer - New users registered this month",
"new_students_this_month": "integer - New students registered this month",
"new_teachers_this_month": "integer - New teachers created this month",
"students_by_status": {
"verified": "integer - Count of verified students",
"pending": "integer - Count of pending students",
"declined": "integer - Count of declined students",
"suspended_temporary": "integer - Count of temporarily suspended students",
"suspended_permanent": "integer - Count of permanently suspended students"
},
"students_by_grade_school_division": [
{
"grade": "string - Grade name",
"school_type": "string - School type name",
"division": "string - Division name",
"count": "integer - Number of students in this combination"
}
],
"teachers_active_count": "integer - Number of active teachers",
"teachers_inactive_count": "integer - Number of inactive teachers",
"teachers_by_grade_subject": [
{
"grade": "string - Grade name",
"subject": "string - Subject name",
"count": "integer - Number of teachers for this grade/subject"
}
],
"total_subjects": "integer - Total number of subjects",
"total_grades": "integer - Total number of grades",
"total_school_types": "integer - Total number of school types",
"total_divisions": "integer - Total number of divisions",
"total_governorates": "integer - Total number of governorates",
"total_areas": "integer - Total number of areas",
"total_video_security_options": "integer - Total number of video security options"
}
Error Responses:
| Status | Condition | Response Body |
|---|---|---|
401 |
Not authenticated | {"detail": "Authentication credentials were not provided."} |
403 |
Not site owner | {"detail": "You do not have permission to perform this action."} |
Generated from main API documentation. Covers only SiteOwner-accessible endpoints.