The Pragati backend exposes a JSON API rooted at /api. All identifiers are stored as MySQL BIGINT and serialized to strings (via a BigInt.toJSON override). Unless stated otherwise, endpoints require an authenticated caller.
Content-Type: application/jsonfor every request with a body.Authorization: Bearer <token>is required on all protected routes. Tokens are obtained fromPOST /api/auth/loginand are signed withAUTH_JWT_SECRET.x-device-key: <secret>can replace the Authorization header for device/hardware authentication. This is used for RFID readers, ESP32-CAM face recognition devices, and attendance kiosks. Supported endpoints:POST /api/attendance/mark- Bulk RFID attendancePOST /api/attendance/rfid- Single RFID scanPOST /api/attendance/sessions- Create attendance sessionPOST /api/attendance/sessions/:id/records- Add records to sessionPOST /api/face-recognition/attendance/face- Face recognition attendance
- Role enforcement mirrors
users.role(ADMIN,GOVERNMENT,PRINCIPAL,TEACHER,STUDENT). Teachers and principals are scoped to theirschoolId; teachers additionally must matchclassTeacherIdfor student/attendance data, while principals can review school-wide reports and manage timetables for their campus. - Validation failures return
400with{ "message": "Validation failed", "errors": { ... } }. Authorization errors use403, missing records use404, duplicate attendance sessions use409.
- Roles: Public
- Description: Basic readiness probe.
- Response 200
{ "status": "ok" }After running the seed script (npx ts-node scripts/seedMockData.ts), these accounts are available:
| Role | Password | |
|---|---|---|
| ADMIN | admin@mock.test | AdminPass123! |
| GOVERNMENT | government@mock.test | GovPass123! |
| PRINCIPAL | principal@mock.test | PrincipalPass123! |
| TEACHER | teacher@mock.test | TeacherPass123! |
| STUDENT | student@mock.test | StudentPass123! |
- Roles: Public
- Description: Exchange credentials for a JWT used across protected routes.
- Request
{
"email": "admin@mock.test",
"password": "AdminPass123!"
}- Response 200
{
"token": "<jwt>",
"expiresIn": "12h",
"userId": "1",
"role": "ADMIN",
"studentId": null,
"teacherId": null,
"schoolId": "1"
}- Errors:
401invalid credentials,403user blocked.
- Roles:
ADMIN,PRINCIPAL - Description: Create an application user mapped to optional teacher/student records. Principals can only create STUDENT and TEACHER user accounts for staff/students in their own school.
- Request
{
"email": "teacher@mock.test",
"password": "TeacherPass123!",
"phoneNumber": "+15550001002",
"role": "TEACHER",
"schoolId": "1",
"teacherId": "7"
}- Response 201
{
"id": "12",
"email": "teacher@mock.test",
"phoneNumber": "+15550001002",
"role": "TEACHER",
"status": "active",
"studentId": null,
"teacherId": "7",
"schoolId": "1",
"createdAt": "2025-11-16T06:57:00.000Z",
"updatedAt": "2025-11-16T06:57:00.000Z"
}- Notes: Principals must set
roleto either "STUDENT" or "TEACHER" and can only create accounts for staff/students in their school. WhenstudentIdorteacherIdis provided, the system verifies they belong to the principal's school.
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: List platform users. Principals can only view users from their own school.
- Response 200
[
{
"id": "1",
"email": "admin@mock.test",
"phoneNumber": null,
"role": "ADMIN",
"status": "active",
"studentId": null,
"teacherId": null,
"schoolId": "1",
"createdAt": "2025-11-16T06:57:00.000Z",
"updatedAt": "2025-11-16T06:57:00.000Z"
}
]- Roles:
ADMIN - Description: Block/unblock a user account.
- Request
{ "status": "blocked" }- Response 200
{
"id": "12",
"status": "blocked",
"updatedAt": "2025-11-17T03:15:00.000Z"
}All routes require authorization. Teachers automatically scope to their schoolId and may only read individual students when they are the assigned homeroom (classTeacherId).
- Roles:
ADMIN,GOVERNMENT - Description: Register a school campus.
- Request
{ "name": "Central High", "district": "Pune" }- Response 201
{ "id": "1", "name": "Central High", "district": "Pune", "isActive": true, "createdAt": "2025-05-01T09:00:00.000Z" }- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List schools. Teacher/principal callers automatically receive only their campus.
- Response 200
[
### GET `/api/core/schools/:schoolId/settings`
- **Roles**: `ADMIN`, `GOVERNMENT`, `PRINCIPAL`, `TEACHER`
- **Description**: Get school settings for attendance features. Teachers and Principals can only view settings for their own school.
- **Response 200**
```json
{
"schoolId": "1",
"teacherAttendanceEnabled": true,
"faceAttendanceEnabled": true,
"multiFaceAttendanceEnabled": true,
"rfidEnabled": false,
"updatedAt": "2025-12-07T19:10:00.000Z"
}- Response Fields:
Field Type Default Description schoolId string - The school ID teacherAttendanceEnabled boolean true Whether geo-attendance for teachers is enabled faceAttendanceEnabled boolean true Whether face recognition attendance is enabled multiFaceAttendanceEnabled boolean true Whether multi-face detection is enabled (group photos) rfidEnabled boolean true Whether RFID card attendance is enabled updatedAt string - ISO timestamp of last update - Notes: If settings don't exist for a school, default values are returned (all
true). No database record is created until settings are explicitly updated via PUT.
- Roles:
ADMIN,PRINCIPAL - Description: Update school settings for attendance features. Principals can only update settings for their own school.
- Request
{
"teacherAttendanceEnabled": true,
"faceAttendanceEnabled": true,
"multiFaceAttendanceEnabled": false,
"rfidEnabled": true
}-
Parameters (all optional, at least one required):
Field Type Description teacherAttendanceEnabled boolean Enable/disable geo-attendance for teachers faceAttendanceEnabled boolean Enable/disable face recognition attendance multiFaceAttendanceEnabled boolean Enable/disable multi-face detection (group photos) rfidEnabled boolean Enable/disable RFID card attendance -
Response 200
{
"message": "School settings updated successfully",
"schoolId": "1",
"teacherAttendanceEnabled": true,
"faceAttendanceEnabled": true,
"multiFaceAttendanceEnabled": false,
"rfidEnabled": true,
"updatedAt": "2025-12-07T19:15:00.000Z"
}- Errors:
400: At least one field must be provided403: Principals can only update settings for their own school404: School not found
- Notes:
- Settings are created with defaults if they don't exist when updating.
- Use these settings in your mobile app to show/hide attendance features based on what's enabled.
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Create an academic grade level for a school. Principals can create grades in their own school.
- Request
{ "schoolId": "1", "name": "Grade 8", "level": 8 }- Response 201
{ "id": "10", "schoolId": "1", "name": "Grade 8", "level": 8, "isActive": true }- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Bulk create multiple grades at once (e.g., Grade 1 through 12 with one API call). The
nameFormatuses{level}as a placeholder. Principals can only create grades in their own school. - Request
{
"schoolId": "1",
"startLevel": 1,
"endLevel": 12,
"nameFormat": "Grade {level}"
}- Response 201
{
"message": "Created 12 grades",
"count": 12,
"range": { "startLevel": 1, "endLevel": 12 }
}- Notes: Uses
skipDuplicates: true, so existing grades at the same level won't cause errors.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List grades. Optional
schoolIdquery (auto-set for school-scoped users). - Response 200
[
{ "id": "10", "schoolId": "1", "name": "Grade 8", "level": 8 }
]- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Update grade name or toggle active status. Principals can only edit grades in their own school.
- Request
{ "name": "Grade 8 Advanced", "isActive": false }- Response 200
{ "id": "10", "schoolId": "1", "name": "Grade 8 Advanced", "level": 8, "isActive": false }- Notes: All fields are optional.
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Create a section (division) within a grade. Principals can create sections in grades from their own school.
- Request
{ "gradeId": "10", "label": "A" }- Response 201
{ "id": "4", "gradeId": "10", "label": "A" }- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Bulk create multiple sections at once for a grade (e.g., A through F with one API call). Principals can only create sections in grades from their own school.
- Request
{
"gradeId": "10",
"labels": ["A", "B", "C", "D", "E", "F"]
}- Response 201
{
"message": "Created 6 sections",
"count": 6,
"labels": ["A", "B", "C", "D", "E", "F"]
}- Notes: Uses
skipDuplicates: true, so existing sections with the same label won't cause errors. Maximum 26 sections per request. Labels must be unique within the request.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List sections, optionally filtered by
gradeId. - Response 200
[
{ "id": "4", "gradeId": "10", "label": "A" }
]- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Update section label. Principals can only edit sections in their own school.
- Request
{ "label": "B" }- Response 200
{ "id": "4", "gradeId": "10", "label": "B" }- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Create a classroom record linking school, grade, and section. Principals can create classrooms in their own school.
- Request
{ "schoolId": "1", "gradeId": "10", "sectionId": "4", "academicYear": "2025-2026" }- Response 201
{ "id": "25", "schoolId": "1", "gradeId": "10", "sectionId": "4", "academicYear": "2025-2026" }- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List classrooms. Optional
schoolIdquery. Responses embed grade/section to aid UI rendering. - Response 200
[
{
"id": "25",
"schoolId": "1",
"grade": { "id": "10", "name": "Grade 8" },
"section": { "id": "4", "label": "A" },
"academicYear": "2025-2026"
}
]- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Fetch a single classroom by ID with grade, section, and school details.
- Response 200
{
"id": "25",
"schoolId": "1",
"gradeId": "10",
"sectionId": "4",
"academicYear": "2025-2026",
"grade": {
"id": "10",
"name": "Grade 8",
"level": 8
},
"section": {
"id": "4",
"label": "A"
},
"school": {
"id": "1",
"name": "Mock Public School"
}
}- Notes: Teachers and principals can only access classrooms in their own school.
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Update classroom details. Principals can only edit classrooms in their own school.
- Request
{ "academicYear": "2026-2027" }- Response 200
{ "id": "25", "schoolId": "1", "gradeId": "10", "sectionId": "4", "academicYear": "2026-2027" }- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Create a teacher profile. Principals can create teachers in their own school.
- Request
{ "schoolId": "1", "firstName": "Tina", "lastName": "Teacher", "email": "teacher@school.test" }- Response 201
{ "id": "7", "schoolId": "1", "firstName": "Tina", "lastName": "Teacher", "email": "teacher@school.test" }- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List teachers. Optional
schoolId. School-scoped roles only see their campus. - Response 200
[
{ "id": "7", "schoolId": "1", "firstName": "Tina", "lastName": "Teacher" }
]- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Update teacher details. Principals can only edit teachers in their own school.
- Request
{ "firstName": "Updated", "email": "newemail@school.test" }- Response 200
{ "id": "7", "schoolId": "1", "firstName": "Updated", "lastName": "Teacher", "email": "newemail@school.test" }- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Create a subject catalog entry. Principals can create subjects in their own school.
- Request
{ "schoolId": "1", "code": "MATH8", "name": "Mathematics" }- Response 201
{ "id": "3", "schoolId": "1", "code": "MATH8", "name": "Mathematics" }- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List subjects, optionally filtered by
schoolId. - Response 200
[
{ "id": "3", "code": "MATH8", "name": "Mathematics", "schoolId": "1" }
]- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Update subject name or code. Principals can only edit subjects in their own school.
- Request
{ "name": "Advanced Mathematics", "code": "MATH8A" }- Response 200
{ "id": "3", "code": "MATH8A", "name": "Advanced Mathematics", "schoolId": "1" }- Notes: All fields are optional.
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Create a student profile and tie it to a classroom.
gradeLevel,sectionLabel, andenrolledAtare automatically set from the classroom. Principals can create students in their own school. - Request
{
"schoolId": "1",
"classroomId": "25",
"code": "STU-0001",
"phoneNumber": "+15550001001",
"rfidUid": "ABC123456789",
"firstName": "Sanjay",
"lastName": "Student",
"gender": "M",
"dateOfBirth": "2010-05-15",
"classTeacherId": "7"
}- Response 201
{
"id": "45",
"schoolId": "1",
"classroomId": "25",
"code": "STU-0001",
"rfidUid": "ABC123456789",
"firstName": "Sanjay",
"lastName": "Student",
"gender": "M",
"dateOfBirth": "2010-05-15",
"gradeLevel": 8,
"sectionLabel": "A",
"enrolledAt": "2025-11-19",
"active": true
}- Notes:
gender("M", "F", "O"),dateOfBirth,rfidUid, andclassTeacherIdare optional. TherfidUidfield stores the RFID card UID for attendance marking.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List students. Optional
classroomIdquery. Principals see the entire school. Returns classroom information for each student. - Query Parameters: Optional
classroomIdfor filtering by classroom. - Response 200
[
{
"id": "45",
"code": "STU-0001",
"firstName": "Sanjay",
"lastName": "Student",
"dateOfBirth": "2010-05-15",
"gender": "M",
"contact": {
"phone": "+15550001001",
"email": null,
"address": null
},
"user": {
"id": "12",
"email": "student@mock.test"
},
"classroom": {
"id": "25",
"academicYear": "2025-2026",
"grade": { "id": "10", "name": "Grade 8", "level": 8 },
"section": { "id": "4", "label": "A" }
}
}
]- Notes:
- The
userfield is only included when the student has an associated user account. - The
classroomfield is always included and contains grade and section details. - Teacher Access: Teachers can see students from classrooms where they are either:
- The homeroom teacher (
classTeacherId), OR - Assigned to teach a subject (via
TeacherSubject)
- The homeroom teacher (
- The
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL,STUDENT - Description: Fetch a student with related subjects, attendance summaries, classroom, grade, and school details.
- Response 200
{
"id": "45",
"schoolId": "1",
"classroomId": "25",
"code": "STU-0001",
"firstName": "Sanjay",
"lastName": "Student",
"gender": "M",
"dateOfBirth": "2010-05-15",
"gradeLevel": 8,
"sectionLabel": "A",
"active": true,
"classroom": {
"id": "25",
"academicYear": "2025-2026",
"grade": { "id": "10", "name": "Grade 8", "level": 8 },
"section": { "id": "4", "label": "A" }
},
"school": { "id": "1", "name": "Mock Public School" },
"subjects": [ { "id": "3", "code": "MATH8", "name": "Mathematics" } ],
"attendances": [ { "sessionDate": "2025-06-10", "status": "present" } ]
}- Notes:
- Students may only fetch their own record.
- Teacher Access: Teachers can view a student if they are either:
- The homeroom teacher (
classTeacherId), OR - Assigned to teach a subject in the student's classroom (via
TeacherSubject)
- The homeroom teacher (
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL - Description: Update student details. When
classroomIdis changed,gradeLevelandsectionLabelare automatically updated. Principals can only edit students in their own school. - Request
{
"classroomId": "26",
"firstName": "Updated",
"phoneNumber": "+15550001002",
"rfidUid": "XYZ987654321",
"gender": "F",
"active": false
}- Response 200
{
"id": "45",
"schoolId": "1",
"classroomId": "26",
"firstName": "Updated",
"rfidUid": "XYZ987654321",
"gradeLevel": 9,
"sectionLabel": "B",
"active": false
}- Notes: All fields are optional. Setting
active: falseeffectively disables the student. SetrfidUidto link an RFID card to the student.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Assign a teacher to a subject-classroom pairing.
- Request
{
"teacherId": "7",
"subjectId": "3",
"classroomId": "25",
"startDate": "2025-06-01",
"endDate": null
}- Response 201
{ "id": "12", "teacherId": "7", "subjectId": "3", "classroomId": "25", "startDate": "2025-06-01", "endDate": null }- Errors:
409if the same teacher-subject-classroom assignment with the same start date already exists. - Notes: Teachers can only manage their own assignments; principals must belong to the same school.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Fetch teacher-subject assignments with teacher, subject, and classroom details. Teachers automatically see only their own assignments; principals see all assignments in their school.
- Query Parameters: Optional
teacherId,classroomId,subjectIdfor filtering. - Response 200
[
{
"id": "12",
"teacherId": "7",
"subjectId": "3",
"classroomId": "25",
"startDate": "2025-06-01",
"endDate": null,
"teacher": { "firstName": "John", "lastName": "Doe" },
"subject": { "code": "MATH101", "name": "Mathematics" },
"classroom": { "grade": { "name": "Grade 8" }, "section": { "label": "A" } }
}
]- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Link a student to a teacher-subject record.
- Request
{ "studentId": "45", "teacherSubjectId": "12", "enrolledOn": "2025-06-05", "status": "active" }- Response 201
{ "id": "33", "studentId": "45", "teacherSubjectId": "12", "status": "active" }- Errors:
409if the student is already enrolled in this subject.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Fetch student-subject enrollments with student, teacher, subject, and classroom details. Teachers see only their own assignments; principals see all enrollments in their school.
- Query Parameters: Optional
studentId,teacherSubjectId,classroomIdfor filtering. - Response 200
[
{
"id": "33",
"studentId": "45",
"teacherSubjectId": "12",
"status": "active",
"enrolledOn": "2025-06-05",
"student": { "firstName": "John", "lastName": "Doe", "code": "STU-001" },
"teacherSubject": {
"subject": { "name": "Mathematics", "code": "MATH8" },
"teacher": { "firstName": "Jane", "lastName": "Smith" },
"classroom": { "grade": { "name": "Grade 8" }, "section": { "label": "A" } }
}
}
]- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Create a reusable student cohort (manual or dynamic visibility).
- Request
{ "schoolId": "1", "name": "Remediation Batch", "description": "Math help", "visibility": "manual" }- Response 201
{ "id": "3", "schoolId": "1", "name": "Remediation Batch", "visibility": "manual" }- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Add or re-add members to a group. Operation is idempotent.
- Request
{ "studentIds": ["45", "46"], "addedBy": "7" }- Response 200
{ "groupId": "3", "totalMembers": 2 }- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: List student groups (auto-scoped to caller's school unless admin/government).
- Query: optional
schoolId - Response 200
[
{ "id": "3", "schoolId": "1", "name": "Remediation Batch", "members": [ { "studentId": "45" } ] }
]Homeroom teachers (matching classTeacherId) or devices with x-device-key can manage attendance. Each classroom may have only one session per day.
Hardware devices (RFID readers, ESP32-CAM, kiosks) can mark attendance without user login by using the x-device-key header:
| Endpoint | Device Use Case |
|---|---|
POST /api/attendance/rfid |
RFID reader - single tag scan |
POST /api/attendance/mark |
RFID reader - batch scans |
POST /api/face-recognition/attendance/face |
ESP32-CAM - face recognition |
Setup: Set ATTENDANCE_DEVICE_KEY in your .env file, then include x-device-key: <your-key> header in requests.
- Headers:
Authorization: Bearer <token>(homeroom teacher/principal) orx-device-key - Description: Mark attendance for students in one call. Creates session if needed, then upserts records. Entries can use either
studentIdorrfidUidto identify students. - Request
{
"schoolId": "1",
"classroomId": "25",
"sessionDate": "2025-06-10",
"entries": [
{ "studentId": "45", "status": "present" },
{ "rfidUid": "ABC123456789", "status": "present" },
{ "studentId": "46", "status": "absent" }
]
}- Response 200
{ "message": "Attendance marked successfully", "sessionId": "90" }- Errors:
400if anyrfidUidcannot be found ({ "message": "Some RFID UIDs could not be found", "notFoundUids": ["UNKNOWN123"] }). - Notes:
sessionDateis optional (defaults to today). Each entry must have eitherstudentIdorrfidUid.- Devices with
x-device-keycan use RFID UIDs to mark attendance directly. - Manual Override: This endpoint provides full control - teachers/principals can set any status regardless of previous automated attendance. Use this to correct mistakes or override automated entries.
- Headers:
Authorization: Bearer <token>(homeroom teacher/principal) orx-device-key - Description: Mark attendance for a single RFID scan. Designed for RFID readers/devices that send one tag at a time. Automatically looks up the student's classroom and creates a session if needed.
- Request
{
"rfidUid": "ABC123456789",
"status": "present"
}-
Parameters:
Field Required Description rfidUid Yes The RFID card UID to look up status No Attendance status: "present" (default), "absent", "late", "excused" schoolId No Override school ID (optional, uses student's school by default) classroomId No Override classroom ID (optional, uses student's classroom by default) -
Response 200
{
"message": "Attendance marked successfully",
"student": {
"id": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"classroom": "Grade 5 - A"
},
"attendance": {
"sessionId": "90",
"sessionDate": "2025-12-03",
"status": "present",
"markedAt": "2025-12-03T08:30:00.000Z"
}
}- Response 404 (RFID not found)
{
"message": "No student found with this RFID UID",
"rfidUid": "UNKNOWN123"
}- Response 400 (student inactive)
{
"message": "Student is inactive",
"rfidUid": "ABC123456789",
"studentId": "45"
}- Notes:
- This endpoint is optimized for single-scan RFID devices
- Automatically creates an attendance session for today if none exists
- The student's enrolled classroom is used by default
- Devices with
x-device-keycan call this without user authentication - Union Behavior: RFID scans will only upgrade attendance (e.g., absent → present), never downgrade. If a student is already marked present (by face recognition or another RFID scan), subsequent scans won't change their status. This ensures multiple automated sources work together.
- Headers:
Authorization: Bearer <token>(homeroom teacher) orx-device-key - Description: Start a new attendance session for a classroom/date pair.
- Request
{
"schoolId": "1",
"classroomId": "25",
"sessionDate": "2025-06-10",
"startsAt": "2025-06-10T09:00:00.000Z",
"endsAt": "2025-06-10T10:00:00.000Z"
}- Response 201
{ "id": "90", "classroomId": "25", "sessionDate": "2025-06-10", "startsAt": "2025-06-10T09:00:00.000Z" }- Errors:
409if a session already exists for[classroomId, sessionDate].
- Roles: Homeroom teachers, principals, admins, government, or device with
x-device-key - Description: Bulk upsert attendance entries for a session. Entries can use either
studentIdorrfidUidto identify students. - Request
{
"entries": [
{ "studentId": "45", "status": "present" },
{ "studentId": "46", "status": "absent" },
{ "rfidUid": "ABC123456789", "status": "present" }
]
}- Response 200
{ "message": "Attendance synced" }- Errors:
400if anyrfidUidcannot be found ({ "message": "Some RFID UIDs could not be found", "notFoundUids": ["UNKNOWN123"] }). - Notes: Teachers can only modify records within 24 hours of the
sessionDate; after the window closes the API responds with403. Each entry must have eitherstudentIdorrfidUid(at least one required).
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL,STUDENT - Description: Raw attendance records for a student within an optional date range.
- Query: optional
from,toISO dates. - Response 200
[
{
"sessionId": "90",
"status": "present",
"attendanceSession": {
"sessionDate": "2025-06-10",
"startsAt": "2025-06-10T09:00:00.000Z",
"endsAt": "2025-06-10T10:00:00.000Z"
}
}
]- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL,STUDENT - Description: Aggregate attendance stats for a student looked up by ID or phone.
- Query:
studentId=<id>orphone=<E.164> - Response 200
{
"studentId": "45",
"today": { "total": 1, "present": 1, "absent": 0, "late": 0, "excused": 0, "attendanceRate": 1 },
"thisWeek": { "total": 5, "present": 4, "absent": 1, "late": 0, "excused": 0, "attendanceRate": 0.8 },
"overall": { "total": 120, "present": 110, "absent": 8, "late": 1, "excused": 1, "attendanceRate": 0.92 }
}- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Daily classroom roll-up plus per-student statuses. Teachers can view classrooms they homeroom or teach via subject assignments; only homeroom teachers receive
canEdit=trueduring the 24-hour edit window. - Query: optional
date=YYYY-MM-DD(normalized to midnight). - Response 200
{
"sessions": [
{
"id": "90",
"sessionDate": "2025-06-10",
"editableUntil": "2025-06-11T00:00:00.000Z",
"canEdit": true,
"studentAttendance": [
{
"studentId": "45",
"status": "present",
"student": { "id": "45", "firstName": "Sanjay", "lastName": "Student", "code": "STU-0001" }
}
]
}
],
"summary": { "total": 30, "totals": { "present": 25, "absent": 3, "late": 1, "excused": 1 } }
}- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Lightweight list of all attendance sessions for the classroom with edit-state metadata for UI calendars.
- Response 200
{
"classroomId": "25",
"sessions": [
{
"id": "90",
"sessionDate": "2025-06-10",
"startsAt": "2025-06-10T09:00:00.000Z",
"endsAt": "2025-06-10T10:00:00.000Z",
"totalRecords": 30,
"editableUntil": "2025-06-11T00:00:00.000Z",
"canEdit": true
}
]
}- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Retrieve a single attendance register (students + statuses) for a given session date. Response echoes
canEditso the UI can toggle edit mode based on the 24-hour rule. - Response 200
{
"id": "90",
"classroomId": "25",
"sessionDate": "2025-06-10",
"startsAt": "2025-06-10T09:00:00.000Z",
"endsAt": "2025-06-10T10:00:00.000Z",
"classroom": {
"id": "25",
"grade": { "id": "10", "name": "Grade 8", "level": 8 },
"section": { "id": "4", "label": "A" }
},
"studentAttendance": [
{
"studentId": "45",
"status": "present",
"student": { "id": "45", "firstName": "Sanjay", "lastName": "Student", "code": "STU-0001" }
}
],
"editableUntil": "2025-06-11T00:00:00.000Z",
"canEdit": false
}- Roles:
PRINCIPAL,GOVERNMENT,ADMIN - Description: Generates an attendance summary for every classroom in the principal’s school between the supplied
startandenddates (ISO strings). Useful for exporting figures to the government portal. - Query:
start=YYYY-MM-DD,end=YYYY-MM-DD - Response 200
{
"schoolId": "1",
"range": { "start": "2025-06-01T00:00:00.000Z", "end": "2025-06-30T23:59:59.999Z" },
"totals": {
"sessions": 45,
"totalRecords": 1350,
"present": 1230,
"absent": 90,
"late": 20,
"excused": 10,
"attendanceRate": 0.91
},
"classrooms": [
{
"classroomId": "25",
"grade": { "id": "10", "name": "Grade 8", "level": 8 },
"section": { "id": "4", "label": "A" },
"totalSessions": 15,
"totalRecords": 450,
"present": 420,
"absent": 20,
"late": 5,
"excused": 5,
"attendanceRate": 0.93
}
],
"topClassrooms": [ { "classroomId": "25", "attendanceRate": 0.93, "totalRecords": 450 } ],
"bottomClassrooms": [ { "classroomId": "29", "attendanceRate": 0.82, "totalRecords": 420 } ],
"generatedAt": "2025-06-30T12:05:00.000Z"
}- Roles:
PRINCIPAL,GOVERNMENT,ADMIN - Description: Same payload as the JSON endpoint but streamed as a PDF (content type
application/pdf). Frontends should pass the samestart/endquery parameters.
- Roles:
TEACHER - Description: Returns rich attendance analytics for the authenticated teacher’s classrooms, including per-classroom roles (homeroom vs subject support) and daily trend points for plotting charts. Optional
classroomIdquery restricts output to a single class. - Query:
start=YYYY-MM-DD,end=YYYY-MM-DD, optionalclassroomId=<id> - Response 200
{
"teacherId": "7",
"schoolId": "1",
"range": { "start": "2025-06-01T00:00:00.000Z", "end": "2025-06-30T23:59:59.999Z" },
"classrooms": [
{
"classroomId": "25",
"grade": { "id": "10", "name": "Grade 8", "level": 8 },
"section": { "id": "4", "label": "A" },
"roles": {
"homeroom": true,
"subjects": [ { "subjectId": "3", "subjectCode": "MATH8", "subjectName": "Mathematics" } ]
},
"totalSessions": 12,
"totalRecords": 360,
"present": 332,
"absent": 20,
"late": 6,
"excused": 2,
"attendanceRate": 0.92,
"trend": [
{
"date": "2025-06-05T00:00:00.000Z",
"present": 28,
"absent": 2,
"late": 0,
"excused": 0,
"attendanceRate": 0.93
}
]
}
],
"generatedAt": "2025-06-30T12:05:00.000Z"
}- Roles:
TEACHER - Description: Streams the teacher report as a PDF, mirroring the JSON query parameters and scoping rules. Ideal for downloading/shareable documents that include summary lines for each classroom.
Supported categories (enum ComplaintCategory): lack_of_proper_drinking_water, toilets, girls_toilets, liberty, proper_electricity, computers. Complaint statuses flow through open → in_progress → resolved|dismissed.
- Roles:
STUDENT - Description: Students submit a complaint. By default their profile (name/classroom) is attached, but sending
isAnonymous=truehides it from principals. - Body
{
"category": "computers",
"description": "Only 3 of the lab machines power on. We need working systems before the practical exams.",
"isAnonymous": false
}- Response 201 (student always sees their own details)
{
"id": "42",
"category": "computers",
"description": "Only 3 of the lab machines power on...",
"status": "open",
"isAnonymous": false,
"classroomId": "25",
"student": { "id": "300", "firstName": "Rekha", "lastName": "Yadav", "code": "STU-300" },
"classroom": {
"id": "25",
"grade": { "id": "10", "name": "Grade 8", "level": 8 },
"section": { "id": "4", "label": "A" }
},
"resolutionNote": null,
"resolvedBy": null,
"createdAt": "2025-11-16T09:12:00.000Z"
}- Roles:
STUDENT - Description: Lists the logged-in student's complaints. Optional
statusquery narrows results to a single state. - Response 200
{
"total": 2,
"items": [
{
"id": "42",
"category": "computers",
"status": "in_progress",
"description": "Only 3 of the lab machines power on...",
"student": { "id": "300", "firstName": "Rekha", "lastName": "Yadav", "code": "STU-300" },
"classroom": { "id": "25", "grade": { "name": "Grade 8" }, "section": { "label": "A" } },
"resolvedBy": { "id": "9", "role": "PRINCIPAL", "email": "principal@school.in" },
"resolutionNote": "Vendor visit scheduled",
"resolvedAt": "2025-11-19T10:00:00.000Z"
}
]
}- Roles:
PRINCIPAL,ADMIN - Description: Principals automatically scope to their school. Admins must provide
schoolId. Filters includestatus,category,anonymous(true|false|1|0), andstudentId. - Response 200
{
"schoolId": "1",
"total": 3,
"items": [
{
"id": "42",
"category": "computers",
"status": "in_progress",
"isAnonymous": false,
"student": { "id": "300", "firstName": "Rekha", "lastName": "Yadav", "code": "STU-300" },
"classroom": { "id": "25", "grade": { "name": "Grade 8" }, "section": { "label": "A" } },
"resolutionNote": "Vendor visit scheduled",
"resolvedBy": { "id": "9", "role": "PRINCIPAL", "email": "principal@school.in" }
},
{
"id": "45",
"category": "girls_toilets",
"status": "open",
"isAnonymous": true,
"student": null,
"classroom": { "id": "18", "grade": { "name": "Grade 6" }, "section": { "label": "B" } },
"resolutionNote": null,
"resolvedBy": null
}
]
}- Roles:
PRINCIPAL,ADMIN - Description: Update the complaint
statusand/or leave aresolutionNote. Moving toresolvedordismissedautomatically stamps the acting user and timestamp; moving back toopen/in_progressclears those fields. - Body
{
"status": "resolved",
"resolutionNote": "New RO filter installed in Block B"
}- Response 200: Returns the updated complaint payload (student info hidden when
isAnonymous=true).
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Create an assessment definition tied to a classroom and teacher.
- Request
{
"subjectId": "3",
"teacherId": "7",
"classroomId": "25",
"name": "Midterm",
"totalMarks": 100,
"examDate": "2025-07-01"
}- Response 201
{ "id": "15", "subjectId": "3", "teacherId": "7", "classroomId": "25", "examDate": "2025-07-01" }- Notes: Teachers must match
teacherId; principals must share the school.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Bulk upsert scores for an exam.
- Request
{
"examId": "15",
"results": [
{ "studentId": "45", "score": 88, "grade": "B+" },
{ "studentId": "46", "score": 95, "grade": "A" }
]
}- Response 200
{ "message": "Exam results synced", "updated": 2 }- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL,STUDENT - Description: Fetch the 10 most recent exam results for a student with embedded exam metadata.
- Response 200
[
{
"exam": { "id": "15", "name": "Midterm", "totalMarks": 100, "examDate": "2025-07-01" },
"score": 88,
"grade": "B+"
}
]- Notes: Students may only fetch themselves; teachers/principals/government must share the school.
- Roles:
ADMIN,GOVERNMENT,TEACHER,PRINCIPAL - Description: Create a notification broadcast with explicit targets.
- Request
{
"schoolId": "1",
"title": "PTA Meeting",
"body": "Parents meet on Friday",
"category": "general",
"activeFrom": "2025-06-12T04:00:00.000Z",
"activeTill": "2025-06-15T23:59:59.000Z",
"priority": 3,
"isPublic": false,
"createdBy": "7",
"targets": {
"studentIds": ["45"],
"studentGroupIds": ["3"],
"teacherIds": [],
"classroomIds": ["25"]
}
}- Response 201
{
"notification": {
"id": "20",
"schoolId": "1",
"title": "PTA Meeting",
"priority": 3,
"activeFrom": "2025-06-12T04:00:00.000Z",
"activeTill": "2025-06-15T23:59:59.000Z"
},
"targets": 3
}- Notes: At least one target bucket must contain IDs unless
isPublic=true, which publishes the message to the public feed. Teachers/principals must belong to the same school they target.
- Roles: Any authenticated role
- Description: List notifications where
activeFrom <= now <= activeTill. - Response 200
[
{
"id": "20",
"title": "PTA Meeting",
"body": "Parents meet on Friday",
"category": "general",
"priority": 3,
"activeTill": "2025-06-15T23:59:59.000Z",
"targets": [ { "type": "student", "studentId": "45" } ]
}
]- Roles: Public
- Description: Fetch active campus-wide notices flagged as public (a digital notice board). Optional
schoolIdquery scopes to one campus; otherwise returns every public notice. - Response 200
{
"total": 2,
"items": [
{
"id": "90",
"schoolId": "1",
"title": "Science Fair",
"body": "Visitors welcome on Friday 4 PM.",
"category": "general",
"priority": 2,
"isPublic": true,
"activeFrom": "2025-11-18T02:00:00.000Z",
"activeTill": "2025-11-21T23:59:59.000Z"
}
]
}- Roles:
ADMIN,PRINCIPAL - Description: Replace the entire weekly timetable for a classroom. Duplicate
(weekDay, period)pairs are rejected. - Request
{
"entries": [
{
"weekDay": 1,
"period": 1,
"startTime": "09:00",
"endTime": "09:45",
"teacherSubjectId": "42",
"label": "Mathematics",
"location": "Room 201"
},
{
"weekDay": 1,
"period": 2,
"startTime": "09:50",
"endTime": "10:30",
"label": "Advisory"
}
]
}- Response 200
{ "classroomId": "25", "totalEntries": 2 }- Roles:
ADMIN,GOVERNMENT,PRINCIPAL,TEACHER,STUDENT - Description: Fetch the timetable for a classroom with teacher/subject enrichments when linked via
teacherSubjectId. - Response 200
{
"classroomId": "25",
"schoolId": "1",
"entries": [
{
"id": "10",
"weekDay": 1,
"period": 1,
"label": "Mathematics",
"startTime": "09:00",
"endTime": "09:45",
"teacherSubjectId": "42",
"teacher": { "id": "7", "firstName": "Tina", "lastName": "Teacher" },
"subject": { "id": "3", "code": "MATH8", "name": "Mathematics" }
}
]
}- Notes: Teachers must be the homeroom (
classTeacherId); principals can view any classroom; students can view only their assigned classroom.
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL,TEACHER,STUDENT - Description: Convenience endpoint that resolves the student's classroom and returns the same payload as the classroom view.
- Response 200
{
"studentId": "45",
"classroomId": "25",
"entries": [
{
"id": "10",
"weekDay": 1,
"period": 1,
"label": "Mathematics",
"startTime": "09:00",
"endTime": "09:45",
"teacher": { "id": "7", "firstName": "Tina", "lastName": "Teacher" },
"subject": { "id": "3", "code": "MATH8", "name": "Mathematics" }
}
]
}- Notes: Students may only fetch their own timetable; teachers must be the student's homeroom teacher; principals can access any student.
- Roles:
ADMIN,GOVERNMENT,PRINCIPAL,TEACHER - Description: Fetch all timetable slots assigned to a specific teacher across all classrooms. Useful for teacher dashboard views.
- Response 200
[
{
"id": "10",
"dayOfWeek": 1,
"startTime": "09:00",
"endTime": "09:45",
"subject": {
"id": "3",
"name": "Mathematics"
},
"classroom": {
"id": "25",
"grade": { "name": "Grade 8" },
"section": { "label": "A" }
},
"room": "Room 201"
},
{
"id": "15",
"dayOfWeek": 2,
"startTime": "10:00",
"endTime": "10:45",
"subject": {
"id": "3",
"name": "Mathematics"
},
"classroom": {
"id": "26",
"grade": { "name": "Grade 9" },
"section": { "label": "B" }
},
"room": null
}
]- Notes: Teachers can only view their own timetable; principals can view any teacher in their school; admins and government users can view any teacher.
These endpoints allow bulk creation of teachers and students from CSV files. Each imported record automatically gets a user account created with an auto-generated password.
- Roles:
ADMIN,PRINCIPAL - Description: Bulk import teachers from a CSV file. Creates both teacher profiles and corresponding user accounts with auto-generated passwords. Principals can only import teachers to their own school.
- Content-Type:
text/csvorapplication/x-www-form-urlencoded(raw CSV in body) - CSV Format:
firstName,lastName,email,phoneNumber
John,Smith,john.smith@school.edu,+15551234567
Jane,Doe,jane.doe@school.edu,+15559876543-
CSV Columns:
Column Required Description firstName Yes Teacher's first name lastName Yes Teacher's last name email Yes Unique email address (used for login) phoneNumber No Phone number (used for password generation) -
Password Generation: Passwords are auto-generated as
FirstnameLastname@XXXXwhere:Firstnameis the capitalized first nameLastnameis the capitalized last nameXXXXis the last 4 digits of phone number (or1234if no phone provided)- Example:
JohnSmith@4567
-
Request Example (using curl):
curl -X POST "https://api.example.com/api/bulk-import/teachers?schoolId=1" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: text/csv" \
-d "firstName,lastName,email,phoneNumber
John,Smith,john.smith@school.edu,+15551234567
Jane,Doe,jane.doe@school.edu,+15559876543"-
Query Parameters:
Parameter Required Description schoolId Yes (for ADMIN) Target school ID. Principals use their own school automatically. -
Response 201
{
"message": "Bulk import completed",
"imported": 2,
"failed": 0,
"teachers": [
{
"id": "15",
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@school.edu",
"generatedPassword": "JohnSmith@4567"
},
{
"id": "16",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@school.edu",
"generatedPassword": "JaneDoe@6543"
}
],
"errors": []
}- Response with Partial Failures
{
"message": "Bulk import completed with errors",
"imported": 1,
"failed": 1,
"teachers": [
{
"id": "15",
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@school.edu",
"generatedPassword": "JohnSmith@4567"
}
],
"errors": [
{
"row": 2,
"data": { "firstName": "Jane", "lastName": "Doe", "email": "duplicate@school.edu" },
"error": "Email already exists"
}
]
}- Notes:
- The
generatedPasswordis returned in plaintext so administrators can share credentials with teachers. - Store or distribute these passwords securely as they won't be retrievable later.
- Each teacher is created in a transaction with their user account.
- Duplicate emails will fail for that row but won't stop other rows from processing.
- The
- Roles:
ADMIN,PRINCIPAL - Description: Bulk import students from a CSV file. Creates both student profiles and corresponding user accounts with auto-generated passwords. Principals can only import students to their own school.
- Content-Type:
text/csvorapplication/x-www-form-urlencoded(raw CSV in body) - CSV Format:
firstName,lastName,code,classroomId,phoneNumber,gender,dateOfBirth,rfidUid
Amit,Kumar,STU001,25,+919876543210,MALE,2010-05-15,ABC123XYZ
Priya,Sharma,STU002,25,+919876543211,FEMALE,2010-08-22,-
CSV Columns:
Column Required Description firstName Yes Student's first name lastName Yes Student's last name code Yes Unique student code/roll number classroomId Yes ID of the classroom to enroll the student in phoneNumber No Phone number (used for password generation) gender No MALE, FEMALE, or OTHER dateOfBirth No Date of birth in YYYY-MM-DD format rfidUid No RFID card UID for attendance tracking -
Password Generation: Same as teachers -
FirstnameLastname@XXXXwhere XXXX is last 4 digits of phone (or1234).- Example:
AmitKumar@3210
- Example:
-
Request Example (using curl):
curl -X POST "https://api.example.com/api/bulk-import/students?schoolId=1" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: text/csv" \
-d "firstName,lastName,code,classroomId,phoneNumber,gender,dateOfBirth,rfidUid
Amit,Kumar,STU001,25,+919876543210,MALE,2010-05-15,ABC123XYZ
Priya,Sharma,STU002,25,+919876543211,FEMALE,2010-08-22,"-
Query Parameters:
Parameter Required Description schoolId Yes (for ADMIN) Target school ID. Principals use their own school automatically. -
Response 201
{
"message": "Bulk import completed",
"imported": 2,
"failed": 0,
"students": [
{
"id": "101",
"firstName": "Amit",
"lastName": "Kumar",
"code": "STU001",
"classroomId": "25",
"generatedPassword": "AmitKumar@3210"
},
{
"id": "102",
"firstName": "Priya",
"lastName": "Sharma",
"code": "STU002",
"classroomId": "25",
"generatedPassword": "PriyaSharma@3211"
}
],
"errors": []
}- Response with Partial Failures
{
"message": "Bulk import completed with errors",
"imported": 1,
"failed": 1,
"students": [
{
"id": "101",
"firstName": "Amit",
"lastName": "Kumar",
"code": "STU001",
"classroomId": "25",
"generatedPassword": "AmitKumar@3210"
}
],
"errors": [
{
"row": 2,
"data": { "firstName": "Priya", "lastName": "Sharma", "code": "STU001" },
"error": "Student code already exists"
}
]
}- Notes:
- The classroom must exist and belong to the specified school.
gradeLevelandsectionLabelare automatically derived from the classroom.enrolledAtis automatically set to the current date.- The
generatedPasswordis returned in plaintext for distribution to students/parents. - Each student is created in a transaction with their user account.
- Invalid classroomId will cause that row to fail.
These endpoints enable facial recognition-based attendance marking and student photo management. The system uses face-api.js with SSD MobileNet for face detection and a 128-dimensional face descriptor for recognition.
- Roles:
ADMIN,PRINCIPAL,TEACHER - Description: Upload a student's photo and register their face for recognition. The system detects the face, computes a 128-dimensional face descriptor, and stores it for future matching.
- Request
{
"photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}-
Parameters:
Field Required Description photo Yes Base64-encoded image (with or without data URL prefix) -
Response 200
{
"message": "Photo uploaded and face registered successfully",
"student": {
"id": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"hasPhoto": true,
"hasFaceDescriptor": true
}
}- Errors:
400: No face detected in the image400: Invalid image format403: Teachers can only upload photos for students in their assigned classrooms404: Student not found
- Notes:
- The image should contain exactly one clearly visible face.
- For best results, use a front-facing photo with good lighting.
- Teacher Access: Teachers can manage photos for students if they are either:
- The homeroom teacher (
classTeacherId), OR - Assigned to teach a subject in the student's classroom (via
TeacherSubject)
- The homeroom teacher (
- Roles:
ADMIN,PRINCIPAL,TEACHER - Description: Upload multiple photos of a student to train face recognition with better accuracy. The system computes face descriptors from each photo and averages them, which improves recognition reliability across different angles, lighting conditions, and expressions.
- Request
{
"photos": [
"data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"data:image/jpeg;base64,/9j/4BBRSkZJRg...",
"data:image/jpeg;base64,/9j/4CCRSkZJRg..."
]
}-
Parameters:
Field Required Description photos Yes Array of base64-encoded images (max 10 photos) -
Response 200
{
"message": "Face trained successfully with 5 photo(s)",
"student": {
"id": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"hasPhoto": true,
"hasFaceDescriptor": true
},
"training": {
"photosProvided": 6,
"photosSuccessful": 5,
"photosFailed": 1,
"results": [
{ "index": 1, "success": true },
{ "index": 2, "success": true },
{ "index": 3, "success": false, "error": "No face detected" },
{ "index": 4, "success": true },
{ "index": 5, "success": true },
{ "index": 6, "success": true }
]
}
}- Errors:
400: No valid faces detected in any of the provided photos400: Maximum 10 photos allowed per training request403: Teachers can only upload photos for students in their assigned classrooms404: Student not found
- Notes:
- Why multiple photos? Averaging face descriptors from multiple images (different angles, lighting, expressions) creates a more robust face encoding that matches better in real-world conditions.
- Recommended: Upload 3-5 photos with varied lighting and slight angle differences.
- Photos that fail face detection are skipped but don't prevent the training from completing.
- The first successful photo is stored as the reference photoUrl.
- Teacher Access: Teachers can train face data for students if they are either:
- The homeroom teacher (
classTeacherId), OR - Assigned to teach a subject in the student's classroom (via
TeacherSubject)
- The homeroom teacher (
- Roles:
ADMIN,PRINCIPAL,TEACHER - Description: Remove a student's photo and face descriptor data.
- Response 200
{
"message": "Photo and face data removed"
}- Errors:
403: Teachers can only manage photos for students in their assigned classrooms404: Student not found
- Notes:
- Teacher Access: Teachers can delete face data for students if they are either:
- The homeroom teacher (
classTeacherId), OR - Assigned to teach a subject in the student's classroom (via
TeacherSubject)
- The homeroom teacher (
- Teacher Access: Teachers can delete face data for students if they are either:
- Roles:
ADMIN,PRINCIPAL,TEACHER - Description: Check if a student has a registered photo and face descriptor.
- Response 200
{
"studentId": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"hasPhoto": true,
"hasFaceDescriptor": true
}- Roles:
ADMIN,PRINCIPAL,TEACHER - Description: Get an overview of face registration status for all students in a classroom.
- Response 200
{
"classroomId": "25",
"className": "Grade 8 - A",
"totalStudents": 30,
"registeredCount": 25,
"notRegisteredCount": 5,
"registrationPercentage": 83,
"students": [
{
"studentId": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"hasPhoto": true,
"hasFaceDescriptor": true
},
{
"studentId": "46",
"firstName": "Priya",
"lastName": "Sharma",
"code": "STU-0002",
"hasPhoto": false,
"hasFaceDescriptor": false
}
]
}- Roles:
ADMIN,PRINCIPAL,TEACHER(or Device Key) - Description: Mark attendance using facial recognition. Supports multiple faces in a single image (group photo). Automatically creates an attendance session if one doesn't exist for today. Only matches against students enrolled in the specified classroom who have registered face descriptors.
- Content-Type:
multipart/form-data(recommended for devices) ORapplication/json(base64) - Headers:
Authorization: Bearer <token>- Required for user authentication- OR
x-device-key: <device_key>- Alternative for device/kiosk authentication (requiresATTENDANCE_DEVICE_KEYenv variable)
POST /api/face-recognition/attendance/face
Content-Type: multipart/form-data
x-device-key: your-device-key
Form Fields:
- photo: (file) image/jpeg or image/png
- classroomId: "25"
- schoolId: "1"
- sessionDate: "2025-12-02" (optional)
{
"photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"classroomId": "25",
"schoolId": "1",
"sessionDate": "2025-12-02",
"markAbsent": true
}-
Parameters:
Field Required Description photo Yes Base64-encoded image containing student faces. Supports JPEG, PNG, WebP. Can include data URL prefix or raw base64. classroomId Yes ID of the classroom for attendance. Only students in this classroom will be matched. schoolId Yes (for ADMIN or Device Auth) Target school ID. Principals/Teachers use their school automatically. Required when using device key. sessionDate No Date for attendance (defaults to today). Format: YYYY-MM-DD. Cannot be in the future. markAbsent No If true, marks all students NOT detected in the image as ABSENT. Default:false. -
How It Works:
- Image is decoded and processed using face-api.js SSD MobileNet detector
- All faces in the image are detected and 128-dimensional descriptors are computed
- Each detected face is compared against registered students in the classroom
- Euclidean distance is calculated between descriptors (threshold: 0.6)
- Best matching student for each face is identified (if within threshold)
- Attendance session is retrieved or created (ONE session per classroom per day)
- Matched students are marked as "present"
- If
markAbsent=true, all non-matched students are marked as "absent"
-
Response 200 (successful matches)
{
"message": "Attendance marked for 3 student(s), 2 marked absent",
"facesDetected": 4,
"matchedCount": 3,
"unmatchedCount": 1,
"sessionId": "90",
"sessionDate": "2025-12-02",
"markedStudents": [
{
"studentId": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"confidence": 85,
"status": "present"
},
{
"studentId": "46",
"firstName": "Priya",
"lastName": "Sharma",
"code": "STU-0002",
"confidence": 92,
"status": "present"
},
{
"studentId": "47",
"firstName": "Amit",
"lastName": "Kumar",
"code": "STU-0003",
"confidence": 78,
"status": "present"
}
],
"unmatchedFaces": [4],
"absentStudents": [
{
"studentId": "48",
"firstName": "Ravi",
"lastName": "Patel",
"code": "STU-0004"
},
{
"studentId": "49",
"firstName": "Neha",
"lastName": "Singh",
"code": "STU-0005"
}
],
"absentCount": 2
}-
Response Fields:
Field Type Description message string Summary of the operation facesDetected number Total number of faces detected in the image matchedCount number Number of faces successfully matched to students unmatchedCount number Number of faces that couldn't be matched sessionId string ID of the attendance session (created or existing) sessionDate string Date of the attendance session (YYYY-MM-DD) markedStudents array Array of matched students with their details markedStudents[].studentId string Student ID markedStudents[].firstName string Student's first name markedStudents[].lastName string Student's last name markedStudents[].code string Student code/roll number markedStudents[].confidence number Match confidence (0-100%). Higher is better. markedStudents[].status string Always "present" for matched students unmatchedFaces array 1-indexed positions of faces that weren't matched absentStudents array (Only if markAbsent=true) Array of students marked absentabsentStudents[].studentId string Student ID absentStudents[].firstName string Student's first name absentStudents[].lastName string Student's last name absentStudents[].code string Student code/roll number absentCount number (Only if markAbsent=true) Number of students marked absent -
Response 200 (no matches)
{
"message": "No matching students found",
"facesDetected": 2,
"matchedCount": 0,
"unmatchedFaces": [1, 2],
"attendanceMarked": false
}- Response 400 (no faces detected)
{
"message": "No faces detected in the image",
"facesDetected": 0
}- Response 400 (no registered students)
{
"message": "No students with registered face data in this classroom",
"facesDetected": 3
}- Response 400 (invalid image)
{
"message": "Invalid image format"
}- Response 403 (teacher not assigned)
{
"message": "You are not assigned to this classroom"
}- Response 404 (classroom not found)
{
"message": "Classroom not found in the specified school"
}-
Notes:
- Device Authentication: Supports
x-device-keyheader for camera/kiosk devices. RequiresschoolIdin request body. - Matching Algorithm: Uses Euclidean distance between 128-dimensional face descriptors. Threshold is 0.6 - lower distance = better match.
- Confidence Calculation:
confidence = (1 - distance) * 100. A confidence of 60% means distance was 0.4. - Duplicate Prevention: If a student is matched by multiple faces, they're only marked once.
- Classroom Scoping: Only compares against students enrolled in the specified classroom with registered faces.
- One Session Per Day: Each classroom has exactly ONE attendance session per day. Multiple API calls on the same day update the same session, never creating duplicates. This is enforced by a unique constraint on
(classroomId, sessionDate). - Session Auto-Creation: If no attendance session exists for the date, one is created automatically.
- Union Behavior: Face recognition will always upgrade a student to "present" but will NEVER downgrade a "present" status to "absent". If RFID marked a student present and face recognition's
markAbsent=truedoesn't detect them, they remain present. This allows multiple automated sources (RFID + Face) to work together harmoniously. - Mark Absent Mode: When
markAbsent=true, students NOT detected are marked absent ONLY if they're not already present. Students already marked present (by RFID, previous face scan, etc.) are skipped and logged. - Image Quality: For best results, use well-lit images where faces are clearly visible and front-facing.
- Group Photos: Can process classroom group photos - all visible faces will be detected and matched.
- Access Control: Teachers must be homeroom teacher or have a subject assignment in the classroom (bypassed for device auth).
- Console Logging: The backend logs recognized student names and match statistics to the console for debugging.
- Manual Override: Teachers can use
/attendance/markor/attendance/sessions/:id/recordsto manually override any automated attendance with full control.
- Device Authentication: Supports
-
Example Use Cases:
- Classroom Entry: Camera at door captures students entering, marks them present (use device key)
- Group Photo: Teacher takes a class photo, all visible students marked present
- Individual Check-in: Student scans their face at a kiosk (use device key)
- Automated Attendance: Periodic camera snapshots throughout class for continuous presence tracking
- End-of-Class Finalization: Final photo with
markAbsent=trueto mark remaining students absent
- Roles:
ADMIN,PRINCIPAL,TEACHER - Description: Identify faces in a photo without marking attendance. Useful for testing face registration, verifying student identity, or debugging recognition issues. Returns all potential matches within the threshold for each detected face.
- Content-Type:
application/json - Request
{
"photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"classroomId": "25",
"schoolId": "1"
}-
Parameters:
Field Required Description photo Yes Base64-encoded image (JPEG, PNG, WebP supported) classroomId No Limit matching to a specific classroom. If omitted, searches all students in scope. schoolId No Limit matching to a specific school. Auto-set for principals/teachers based on their school. -
How It Works:
- All faces in the image are detected
- For each face, all registered students (within scope) are compared
- All matches within the 0.6 threshold are returned, sorted by confidence
- The best match (highest confidence) is highlighted separately
- No attendance is marked - this is read-only
-
Response 200 (faces found with matches)
{
"facesDetected": 2,
"results": [
{
"faceIndex": 1,
"bestMatch": {
"studentId": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"classroom": "Grade 8 - A",
"distance": 0.412,
"confidence": 59
},
"allMatches": [
{
"studentId": "45",
"firstName": "Sanjay",
"lastName": "Student",
"code": "STU-0001",
"classroom": "Grade 8 - A",
"distance": 0.412,
"confidence": 59
},
{
"studentId": "52",
"firstName": "Rahul",
"lastName": "Verma",
"code": "STU-0008",
"classroom": "Grade 8 - B",
"distance": 0.58,
"confidence": 42
}
]
},
{
"faceIndex": 2,
"bestMatch": {
"studentId": "46",
"firstName": "Priya",
"lastName": "Sharma",
"code": "STU-0002",
"classroom": "Grade 8 - A",
"distance": 0.285,
"confidence": 72
},
"allMatches": [
{
"studentId": "46",
"firstName": "Priya",
"lastName": "Sharma",
"code": "STU-0002",
"classroom": "Grade 8 - A",
"distance": 0.285,
"confidence": 72
}
]
}
]
}- Response 200 (faces found but no matches)
{
"facesDetected": 1,
"results": [
{
"faceIndex": 1,
"bestMatch": null,
"allMatches": []
}
]
}- Response 200 (no faces detected)
{
"facesDetected": 0,
"matches": []
}-
Response Fields:
Field Type Description facesDetected number Total number of faces found in the image results array Array of results for each detected face results[].faceIndex number 1-indexed position of the face in the image results[].bestMatch object|null The highest-confidence match, or null if no match results[].allMatches array All students matching within threshold, sorted by confidence allMatches[].studentId string Student ID allMatches[].firstName string Student's first name allMatches[].lastName string Student's last name allMatches[].code string Student code/roll number allMatches[].classroom string Classroom name (e.g., "Grade 8 - A") allMatches[].distance number Euclidean distance (0-1). Lower is better. allMatches[].confidence number Match confidence percentage (0-100) -
Notes:
- Read-Only: This endpoint does NOT mark attendance - use
/attendance/facefor that. - Debugging Tool: Use this to verify face registration quality and troubleshoot matching issues.
- Multiple Matches: Shows ALL matches within threshold, helpful for identifying similar-looking students.
- Cross-Classroom: Without
classroomId, searches across all classrooms the user has access to. - Distance vs Confidence: Distance is the raw Euclidean metric (0-1); confidence is
(1 - distance) * 100. - Threshold: Only matches with distance < 0.6 are returned (confidence > 40%).
- Read-Only: This endpoint does NOT mark attendance - use
-
Example Use Cases:
- Registration Verification: After uploading a student's photo, test with another photo to verify recognition works
- Troubleshooting: If a student isn't being recognized, use identify to see if they're close to the threshold
- Duplicate Detection: Check if a face matches multiple students (possible registration errors)
- Quality Check: Test with different photos to ensure the face descriptor is reliable
Geospatial attendance system for teachers with photo verification and GPS location tracking. All data is stored locally on the server (no database) in the uploads/geo-attendance/ directory.
-
Roles:
TEACHER -
Description: Teacher check-in with selfie photo and GPS location. Prevents duplicate check-ins without checking out first.
-
Content-Type:
multipart/form-data -
Request Fields:
Field Type Required Description photo File ✅ Selfie/photo image (JPEG, PNG, etc.) latitude number ✅ GPS latitude (-90 to 90) longitude number ✅ GPS longitude (-180 to 180) accuracy number ❌ GPS accuracy in meters altitude number ❌ Altitude in meters address string ❌ Reverse-geocoded address from client deviceInfo string ❌ Device model/info for audit notes string ❌ Optional notes -
Response 201
{
"message": "Check-in successful",
"record": {
"id": "geo-1733506123456-abc123def",
"type": "check-in",
"timestamp": "2025-12-06T09:00:00.000Z",
"date": "2025-12-06",
"location": {
"latitude": 28.6139,
"longitude": 77.209,
"accuracy": 10,
"address": "123 School Street, New Delhi"
},
"photoUrl": "/api/geo-attendance/photos/teacher-1-1733506123456.jpg"
}
}- Response 400 (already checked in)
{
"message": "Already checked in. Please check out first.",
"lastCheckIn": "2025-12-06T09:00:00.000Z"
}- Response 400 (validation errors)
{ "message": "Latitude and longitude are required" }
{ "message": "Invalid latitude or longitude values" }
{ "message": "Photo is required" }-
Roles:
TEACHER -
Description: Teacher check-out with selfie photo and GPS location. Requires an active check-in. Returns session duration.
-
Content-Type:
multipart/form-data -
Request Fields: Same as check-in
-
Response 201
{
"message": "Check-out successful",
"record": {
"id": "geo-1733535123456-xyz789",
"type": "check-out",
"timestamp": "2025-12-06T17:00:00.000Z",
"date": "2025-12-06",
"location": {
"latitude": 28.6139,
"longitude": 77.209,
"accuracy": 8,
"address": "123 School Street, New Delhi"
},
"photoUrl": "/api/geo-attendance/photos/teacher-1-1733535123456.jpg"
},
"session": {
"checkInTime": "2025-12-06T09:00:00.000Z",
"checkOutTime": "2025-12-06T17:00:00.000Z",
"duration": {
"hours": 8,
"minutes": 0,
"totalMinutes": 480
}
}
}- Response 400 (no active check-in)
{ "message": "No active check-in found. Please check in first." }-
Roles:
TEACHER -
Description: Get current check-in/out status and today's summary for the authenticated teacher.
-
Response 200 (currently checked in)
{
"date": "2025-12-06",
"isCheckedIn": true,
"lastAction": {
"type": "check-in",
"timestamp": "2025-12-06T09:00:00.000Z",
"location": {
"latitude": 28.6139,
"longitude": 77.209,
"address": "123 School Street, New Delhi"
}
},
"todaySummary": {
"checkInCount": 1,
"checkOutCount": 0,
"totalHoursWorked": 4,
"totalMinutesWorked": 30
}
}- Response 200 (no activity today)
{
"date": "2025-12-06",
"isCheckedIn": false,
"lastAction": null,
"todaySummary": {
"checkInCount": 0,
"checkOutCount": 0,
"totalHoursWorked": 0,
"totalMinutesWorked": 0
}
}-
Roles:
TEACHER -
Description: Get attendance history for the authenticated teacher over a date range.
-
Query Parameters:
Parameter Type Required Default Description startDate string ❌ 7 days ago Start date (YYYY-MM-DD) endDate string ❌ today End date (YYYY-MM-DD) -
Response 200
{
"startDate": "2025-12-01",
"endDate": "2025-12-06",
"totalDays": 5,
"history": [
{
"date": "2025-12-06",
"records": [
{
"id": "geo-1733506123456-abc123",
"type": "check-in",
"timestamp": "2025-12-06T09:00:00.000Z",
"location": { "latitude": 28.6139, "longitude": 77.209, "address": "..." },
"photoUrl": "/api/geo-attendance/photos/teacher-1-1733506123456.jpg"
},
{
"id": "geo-1733535123456-xyz789",
"type": "check-out",
"timestamp": "2025-12-06T17:00:00.000Z",
"location": { "latitude": 28.6139, "longitude": 77.209, "address": "..." },
"photoUrl": "/api/geo-attendance/photos/teacher-1-1733535123456.jpg"
}
],
"totalMinutesWorked": 480
},
{
"date": "2025-12-05",
"records": [...],
"totalMinutesWorked": 450
}
]
}- Roles:
ADMIN,PRINCIPAL,TEACHER - Description: Retrieve attendance photo by filename. Teachers can only view their own photos. Admins and Principals can view all photos (Principals scoped to their school).
- Authentication: Supports both
Authorization: Bearer <token>header AND?token=<jwt>query parameter (for direct image URLs in<img>tags).
Example URLs:
# With header (API calls)
GET /api/geo-attendance/photos/teacher-1-1733506123456.jpg
Authorization: Bearer <jwt>
# With query parameter (image src, mobile apps)
GET /api/geo-attendance/photos/teacher-1-1733506123456.jpg?token=<jwt>
- Response 200: Image file (binary)
- Response 401:
{ "message": "Missing or invalid Authorization header" }(no token provided) - Response 404:
{ "message": "Photo not found" } - Response 403:
{ "message": "Access denied" }(teacher trying to view another teacher's photo)
Mobile App Usage:
// When displaying the photo, append the token as a query parameter
const photoUrl = `${record.photoUrl}?token=${authToken}`;
// Use this URL in Image component-
Roles:
ADMIN,PRINCIPAL -
Description: View all teacher attendance records for a specific date. Principals only see teachers from their school.
-
Query Parameters:
Parameter Type Required Default Description date string ❌ today Date to view (YYYY-MM-DD) teacherId string ❌ all Filter by specific teacher ID -
Response 200
{
"date": "2025-12-06",
"totalTeachers": 12,
"teachers": [
{
"teacherId": "1",
"teacherName": "Amit Kumar",
"schoolName": "Delhi Public School",
"firstCheckIn": "2025-12-06T08:45:00.000Z",
"lastCheckOut": "2025-12-06T17:15:00.000Z",
"isCurrentlyCheckedIn": false,
"totalHoursWorked": 8,
"totalMinutesWorked": 30,
"recordCount": 2,
"records": [
{
"id": "geo-...",
"type": "check-in",
"timestamp": "2025-12-06T08:45:00.000Z",
"location": { "latitude": 28.6139, "longitude": 77.209, "address": "..." },
"photoUrl": "/api/geo-attendance/photos/..."
},
{
"id": "geo-...",
"type": "check-out",
"timestamp": "2025-12-06T17:15:00.000Z",
"location": { "latitude": 28.6139, "longitude": 77.209, "address": "..." },
"photoUrl": "/api/geo-attendance/photos/..."
}
]
},
{
"teacherId": "2",
"teacherName": "Priya Singh",
"schoolName": "Delhi Public School",
"firstCheckIn": "2025-12-06T09:00:00.000Z",
"lastCheckOut": null,
"isCurrentlyCheckedIn": true,
"totalHoursWorked": 5,
"totalMinutesWorked": 15,
"recordCount": 1,
"records": [...]
}
]
}-
Roles:
ADMIN,PRINCIPAL -
Description: Get attendance summary statistics for a date range. Useful for reports.
-
Query Parameters:
Parameter Type Required Description startDate string ✅ Start date (YYYY-MM-DD) endDate string ✅ End date (YYYY-MM-DD) -
Response 200
{
"startDate": "2025-12-01",
"endDate": "2025-12-06",
"totalTeachers": 15,
"summary": [
{
"teacherId": "1",
"teacherName": "Amit Kumar",
"daysPresent": 5,
"totalHoursWorked": 42,
"averageHoursPerDay": 8.4
},
{
"teacherId": "2",
"teacherName": "Priya Singh",
"daysPresent": 6,
"totalHoursWorked": 48,
"averageHoursPerDay": 8.0
}
]
}- Response 400
{ "message": "startDate and endDate are required" }- Photos: Stored in
uploads/geo-attendance/directory with naming formatteacher-{teacherId}-{timestamp}.{ext} - Records: Stored in
uploads/geo-attendance/attendance-log.json(simple JSON file, no database) - Cleanup: Consider implementing periodic cleanup of old photos/records for production use
- GPS Permissions: Request location permissions before check-in/check-out
- Camera Access: Use front camera for selfie photos
- Offline Handling: Queue check-in/check-out requests if offline, sync when connected
- Address Geocoding: Use device's reverse geocoding to populate the
addressfield - Status Check: Call
/statuson app launch to determine button state (Check In vs Check Out) - Background Location: Consider background location for geofencing (detect when teacher arrives at school)
| Status | Meaning | Example |
|---|---|---|
| 400 | Schema validation failed | { "message": "Validation failed", "errors": { ... } } |
| 401 | Login failure | { "message": "Invalid credentials" } |
| 403 | Caller lacks role/scope | { "message": "Forbidden" } |
| 404 | Entity missing | { "message": "Student not found" } |
| 409 | Duplicate attendance session | { "message": "Attendance already exists for this classroom on the selected date" } |
| 500 | Unhandled error | { "message": "Something went wrong" } |
Refer to the Zod schemas in src/modules/** for the authoritative field definitions used by each route.