EngineeringAPI contracts

API contracts

Reference for the NexSchool REST API. All endpoints live under /api, organized by module. Tenant routes require an access token plus a tenant identifier. This page keeps every module and endpoint; verbose example bodies are trimmed to one representative shape per module where the source repeated them.

Conventions

Base path

All APIs are served under /api.

Auth headers

Required on all tenant-scoped routes:

Authorization: Bearer <access_token>
X-Tenant-ID: <tenant_id>
Content-Type: application/json

The tenant may alternatively be resolved from the request subdomain.

Success envelope

{ "success": true, "data": {} }

Error envelope

{ "success": false, "error": "ErrorType", "message": "Human-readable message" }

Pagination

List endpoints wrap results with a pagination block:

{
  "success": true,
  "data": [],
  "pagination": { "page": 1, "pageSize": 20, "totalItems": 100, "totalPages": 5 }
}

Common query params across list endpoints: page, pageSize, search, sort, order.

Health

Unauthenticated liveness probe.

MethodPathPurpose
GET/api/healthService health check
{ "status": "healthy", "service": "School ERP Backend", "version": "1.0.0" }

Auth (/api/auth)

MethodPathAuthPurpose
POST/api/auth/loginpublicAuthenticate, return access token + user context
POST/api/auth/logoutrequiredInvalidate session
POST/api/auth/refreshX-Refresh-Token headerIssue new access token
GET/api/auth/profilerequiredGet current user profile
PUT/api/auth/profilerequiredUpdate first_name, last_name, phone
POST/api/auth/profile/picturerequiredUpload profile picture (multipart/form-data, jpeg/png, max 5MB)
POST/api/auth/password/forgotpublicSend reset email
POST/api/auth/password/resetpublicReset password with token
GET/api/auth/email/verify?token=<token>publicVerify email via link
POST/api/auth/email/verifypublicVerify email with { "token": "..." }
GET/api/auth/enabled-featuresrequiredList tenant’s enabled feature flags

Logintenant_id may be replaced with subdomain. The refresh token is returned in the X-Refresh-Token response header.

{
  "email": "admin@school.com",
  "password": "password123",
  "tenant_id": 1
}
{
  "success": true,
  "data": {
    "access_token": "eyJ...",
    "user": {
      "id": 1,
      "email": "admin@school.com",
      "first_name": "John",
      "last_name": "Doe",
      "profile_picture_url": null
    },
    "permissions": ["student.read", "teacher.read"],
    "enabled_features": ["attendance", "fees_management"]
  }
}

Refresh — send the refresh token in the header:

POST /api/auth/refresh
X-Refresh-Token: <refresh_token>

Students (/api/students)

MethodPathAuthPurpose
GET/api/studentsstudent.readList students (?page, pageSize, search, class_id, academic_year_id, is_active)
GET/api/students/<student_id>student.readGet one student
POST/api/studentsstudent.createCreate student (optionally a login account)
PUT/api/students/<student_id>student.updateUpdate any student fields
DELETE/api/students/<student_id>student.deleteSoft delete (sets deleted_at)
GET/api/students/me/dashboardstudent roleSelf dashboard (attendance, fees, schedule)
POST/api/students/promotestudent.updateBulk promote students to a class/year
POST/api/students/promotion/previewrequiredPreview which students will be promoted
GET/api/students/<student_id>/documentsrequiredList student documents
POST/api/students/<student_id>/documentsrequiredUpload document (multipart/form-data)
DELETE/api/students/<student_id>/documents/<document_id>requiredDelete a document
GET/api/students/<student_id>/documents/<document_id>/filerequiredDownload document file stream

Create student — set create_user_account: true to provision a login (then email is required):

{
  "first_name": "Arun",
  "last_name": "Kumar",
  "roll_number": "2024001",
  "admission_number": "ADM001",
  "date_of_birth": "2010-05-15",
  "gender": "male",
  "class_id": 5,
  "grade_id": 3,
  "parent_name": "Suresh Kumar",
  "parent_phone": "+91-9876543210",
  "parent_email": "suresh@email.com",
  "address": "123 Main Street",
  "create_user_account": true,
  "email": "arun@school.com"
}

Bulk promote:

{ "student_ids": [1, 2, 3], "to_class_id": 10, "to_academic_year_id": 2 }

Upload documentmultipart/form-data fields: file (binary), document_type_id, description.

Teachers (/api/teachers)

MethodPathPurpose
GET/api/teachersList teachers (?search, is_active)
POST/api/teachersCreate teacher
GET/api/teachers/<id>Get teacher
PUT/api/teachers/<id>Update teacher
DELETE/api/teachers/<id>Delete teacher
GET/api/teachers/meCurrent teacher profile
GET/api/teachers/me/today-scheduleToday’s schedule (timetable.read)
GET/api/teachers/me/classesClasses assigned to this teacher
POST/api/teachers/leavesApply for leave
GET/api/teachers/<id>/leavesList leaves (?status, academic_year_id)
PUT/api/teachers/leaves/<leave_id>Approve/reject leave (admin)
DELETE/api/teachers/leaves/<leave_id>Delete leave request
GET/api/teachers/<id>/availabilityGet availability grid
PUT/api/teachers/<id>/availabilitySet availability slots
GET/api/teachers/<id>/workloadCurrent period assignment count
PUT/api/teachers/<id>/workload-ruleSet max periods per day/week

Apply for leave:

{ "leave_type": "sick", "start_date": "2025-01-10", "end_date": "2025-01-12", "reason": "..." }

Set availability — array of slots:

[{ "day_of_week": 0, "period": 1, "is_available": true }]

Classes (/api/classes)

MethodPathPurpose
GET/api/classesList classes (?academic_year_id, grade_id, search)
POST/api/classesCreate class
GET/api/classes/<id>Get class
PUT/api/classes/<id>Update class
DELETE/api/classes/<id>Delete class
POST/api/classes/copyCopy class structure to a new academic year
GET/api/classes/<id>/studentsStudents in class
POST/api/classes/<id>/studentsAdd student ({ "student_id": 5 })
DELETE/api/classes/<id>/students/<student_id>Remove student from class
GET/api/classes/<id>/unassigned-studentsStudents not in this class
GET/api/classes/<id>/teachersTeachers in class
POST/api/classes/<id>/teachersAssign teacher to class
DELETE/api/classes/<id>/teachers/<teacher_id>Remove teacher from class
GET/api/classes/<id>/unassigned-teachersTeachers not in this class
POST/api/classes/subjects/by-gradeApply standard subject template for a grade
GET/api/classes/meta/available-class-teachersTeachers not yet assigned as class teacher

Assign teacher:

{ "teacher_id": 3, "subject": "Mathematics", "is_class_teacher": false }

Subjects by grade{ "grade_id": 2, "class_ids": [1, 2, 3] }.

Subjects (/api/subjects)

MethodPathPurpose
GET/api/subjectsList subjects (?search, subject_context_id)
POST/api/subjectsCreate subject ({ "name", "code", "subject_context_id" })
GET/api/subjects/<id>Get subject
PUT/api/subjects/<id>Update subject
DELETE/api/subjects/<id>Delete subject

Class Subjects (/api/class-subjects)

MethodPathPurpose
GET/api/class-subjectsList (?class_id, academic_year_id)
POST/api/class-subjectsAttach subject to class ({ "class_id", "subject_id", "periods_per_week" })
DELETE/api/class-subjects/<id>Detach subject from class

Attendance (/api/attendance)

MethodPathAuthPurpose
POST/api/attendance/markattendance.markMark attendance for a class on a date
GET/api/attendance/my-classesrequiredClasses the current user can mark
GET/api/attendance/class/<class_id>requiredClass attendance (?date, from_date, to_date, page)
GET/api/attendance/student/<student_id>requiredStudent attendance (?from_date, to_date, academic_year_id)
GET/api/attendance/mestudent roleCurrent user’s own attendance
GET/api/attendance/calendar-holidaysrequiredAttendance merged with holiday calendar (?from_date, to_date)

Mark attendance — status values: present, absent, late.

{
  "class_id": 5,
  "date": "2025-01-15",
  "records": [
    { "student_id": 1, "status": "present" },
    { "student_id": 2, "status": "absent" },
    { "student_id": 3, "status": "late" }
  ]
}

Academics (/api/academics)

MethodPathPurpose
GET/api/academics/yearsList academic years
POST/api/academics/yearsCreate academic year
GET/api/academics/years/<id>Get academic year
PUT/api/academics/years/<id>Update academic year
DELETE/api/academics/years/<id>Delete academic year
GET/api/academics/overviewCounts overview (?academic_year_id)

Academic Terms (/api/academic-terms)

MethodPathPurpose
GET/api/academic-termsList terms (?academic_year_id)
POST/api/academic-termsCreate term
GET/api/academic-terms/<id>Get term
PUT/api/academic-terms/<id>Update term
DELETE/api/academic-terms/<id>Delete term

Finance (/api/finance)

MethodPathPurpose
GET/api/finance/fee-structuresList fee structures (?academic_year_id)
POST/api/finance/fee-structuresCreate fee structure
GET/api/finance/fee-structures/<id>Get fee structure
PUT/api/finance/fee-structures/<id>Update fee structure
DELETE/api/finance/fee-structures/<id>Delete fee structure
POST/api/finance/fee-structures/<id>/componentsAdd a fee component
DELETE/api/finance/fee-structures/<id>/components/<component_id>Remove a fee component
POST/api/finance/fee-structures/<id>/assign-classesAssign structure to classes
GET/api/finance/student-feesList student fees (?student_id, class_id, status)
POST/api/finance/student-fees/generateGenerate fees from a structure
GET/api/finance/student-fees/<id>Get a student fee
POST/api/finance/paymentsRecord a payment
GET/api/finance/summaryTotals (?academic_year_id)

Add fee component{ "name": "Tuition Fee", "amount": 5000, "frequency": "quarterly" }.

Record payment:

{
  "student_fee_id": 5,
  "amount": 2000,
  "payment_date": "2025-01-15",
  "payment_method": "cash",
  "reference_number": "TXN001"
}

Fees (/api/fees)

Invoice-based billing (distinct from /api/finance fee structures).

MethodPathPurpose
GET/api/fees/invoicesList invoices (?student_id, status, page)
POST/api/fees/invoicesCreate invoice with line items
GET/api/fees/invoices/<id>Get invoice
PUT/api/fees/invoices/<id>Update invoice
DELETE/api/fees/invoices/<id>Delete invoice
GET/api/fees/invoices/<id>/downloadDownload PDF receipt (WeasyPrint)
POST/api/fees/invoices/<id>/send-reminderSend payment reminder
GET/api/fees/paymentsList payments (?student_id, from_date, to_date)
POST/api/fees/paymentsRecord payment against an invoice

Create invoice:

{
  "student_id": 3,
  "due_date": "2025-02-01",
  "items": [
    { "description": "Tuition Fee", "amount": 5000, "quantity": 1 },
    { "description": "Activity Fee", "amount": 500, "quantity": 1 }
  ]
}

Record payment{ "invoice_id": 5, "amount": 5500, "method": "online", "reference_number": "UPI123" }.

Timetable (/api/timetable)

MethodPathAuthPurpose
GET/api/timetable/configrequiredGet timetable config
PUT/api/timetable/configrequiredUpdate config (total_periods, lunch_start_period, period_duration, start_time)
POST/api/timetable/generaterequire_setup_complete, require_active_subscriptionAuto-generate timetable
GET/api/timetable/slotsrequiredList slots (?class_id, teacher_id, academic_year_id)
POST/api/timetable/slotsrequiredCreate a slot
PUT/api/timetable/slots/<id>requiredUpdate a slot
DELETE/api/timetable/slots/<id>requiredDelete a slot
PATCH/api/timetable/slots/<id>/moverequiredMove slot ({ "day_of_week", "period" })
POST/api/timetable/check-conflictsrequiredDetect class/teacher conflicts

Create slot{ "class_id": 1, "subject_id": 3, "teacher_id": 2, "day_of_week": 0, "period": 1 }.

Check conflicts:

{ "class_id": 1, "teacher_id": 2, "day_of_week": 0, "period": 1, "exclude_slot_id": null }
{ "has_conflict": false, "conflicts": [] }

Schedule (/api/schedule)

MethodPathAuthPurpose
GET/api/schedule/todayrequiredToday’s slots for the current user (teacher’s classes / student’s class)
GET/api/schedule/class/<class_id>requiredSlots for a specific date (?date)

Notifications (/api/notifications)

MethodPathAuthPurpose
GET/api/notificationsrequiredList notifications (?is_read, page)
DELETE/api/notifications/<id>requiredDelete notification
POST/api/notifications/<id>/readrequiredMark one read
POST/api/notifications/read-allrequiredMark all read
POST/api/notifications/sendnotification.sendSend to users/roles/all
GET/api/notifications/streamrequiredSSE stream (long-lived connection)
GET/api/notifications/templatesrequiredList templates
POST/api/notifications/templatesrequiredCreate template
PUT/api/notifications/templates/<id>requiredUpdate template
DELETE/api/notifications/templates/<id>requiredDelete template

Send — target with user_ids, or role_ids, or all: true:

{
  "user_ids": [1, 2, 3],
  "title": "Exam Schedule",
  "message": "Final exams start from 15th January",
  "type": "info",
  "action_url": "/schedule"
}

Stream — the SSE endpoint emits events shaped like data: {"id": 1, "title": "...", "message": "..."}.

Holidays (/api/holidays)

MethodPathPurpose
GET/api/holidaysList holidays (?academic_year_id, from_date, to_date)
POST/api/holidaysCreate holiday ({ "name", "date", "is_recurring" })
PUT/api/holidays/<id>Update holiday
DELETE/api/holidays/<id>Delete holiday

Transport (/api/transport)

Buses

MethodPathPurpose
GET/api/transport/busesList buses
POST/api/transport/busesCreate bus
GET/api/transport/buses/<id>Get bus
GET/api/transport/buses/<id>/detailsBus with route + enrolled student count
PUT/api/transport/buses/<id>Update bus
DELETE/api/transport/buses/<id>Delete bus

Drivers

MethodPathPurpose
GET/api/transport/driversList drivers
POST/api/transport/driversCreate driver
PUT/api/transport/drivers/<id>Update driver
DELETE/api/transport/drivers/<id>Delete driver

Routes & stops

MethodPathPurpose
GET/api/transport/routesList routes
POST/api/transport/routesCreate route
GET/api/transport/routes/<id>Get route
PUT/api/transport/routes/<id>Update route
DELETE/api/transport/routes/<id>Delete route
GET/api/transport/routes/<id>/stopsList stops on a route
POST/api/transport/routes/<id>/stops/reorderReorder route stops
GET/api/transport/stopsList stops
POST/api/transport/stopsCreate stop
PUT/api/transport/stops/<id>Update stop
DELETE/api/transport/stops/<id>Delete stop

Enrollments

MethodPathPurpose
GET/api/transport/enrollmentsList enrollments (?route_id, student_id)
POST/api/transport/enrollEnroll student ({ "student_id", "route_id", "stop_id", "enrollment_type" })
DELETE/api/transport/enroll/<id>Remove enrollment

Staff, schedules & operations

MethodPathPurpose
GET/api/transport/staffList transport staff
POST/api/transport/staffCreate staff
GET/api/transport/staff/<id>/workloadStaff workload
GET/api/transport/schedulesList schedules
POST/api/transport/schedules/exceptionsCreate schedule exception
GET/api/transport/schedules/conflict-checkCheck schedule conflicts
GET/api/transport/bus-assignmentsList bus assignments
POST/api/transport/bus-assignmentsCreate bus assignment
GET/api/transport/dashboardTransport dashboard
GET/api/transport/fee-plansTransport fee plans
GET/api/transport/export/contact-sheetExport contact sheet (CSV/Excel)

Dashboard (/api/dashboard)

MethodPathAuthPurpose
GET/api/dashboardauth_required, tenant_requiredAggregate tenant dashboard
{
  "success": true,
  "data": {
    "total_students": 450,
    "total_teachers": 32,
    "total_classes": 18,
    "attendance_today": { "present": 420, "absent": 30 },
    "fee_summary": { "collected_this_month": 120000, "pending": 45000 },
    "recent_notifications": []
  }
}

RBAC (/api/rbac)

MethodPathPurpose
GET/api/rbac/rolesList roles
POST/api/rbac/rolesCreate role ({ "name", "description", "permission_ids" })
GET/api/rbac/roles/<id>Get role
PUT/api/rbac/roles/<id>Update role
DELETE/api/rbac/roles/<id>Delete role
GET/api/rbac/permissionsList all permission strings
POST/api/rbac/users/<user_id>/rolesAssign role ({ "role_id": 2 })
DELETE/api/rbac/users/<user_id>/roles/<role_id>Revoke role
GET/api/rbac/users/<user_id>/rolesList a user’s roles

Users (/api/users)

MethodPathPurpose
GET/api/usersList users (?search, role_id)
POST/api/usersCreate user
GET/api/users/<id>Get user
PUT/api/users/<id>Update user
DELETE/api/users/<id>Delete user

Platform (/api/platform)

Super-admin only. Not tenant-scoped.

MethodPathPurpose
GET/api/platform/tenantsList tenants
POST/api/platform/tenantsCreate tenant
GET/api/platform/tenants/<id>Get tenant
PUT/api/platform/tenants/<id>Update tenant
DELETE/api/platform/tenants/<id>Delete tenant
POST/api/platform/tenants/<id>/suspendSuspend tenant
POST/api/platform/tenants/<id>/activateActivate tenant
GET/api/platform/plansList plans
POST/api/platform/plansCreate plan
PUT/api/platform/plans/<id>Update plan
DELETE/api/platform/plans/<id>Delete plan
GET/api/platform/notification-settingsGet notification settings
PUT/api/platform/notification-settingsUpdate notification settings
GET/api/platform/audit-logsAudit logs (?tenant_id, action, from_date, to_date)

School setup (/api/school-setup)

MethodPathPurpose
GET/api/school-setup/statusSetup progress ({ is_complete, steps_completed, ... })
POST/api/school-setup/completeMark setup complete
POST/api/school-setup/school-infoSave school info ({ name, address, phone, logo })
POST/api/school-setup/academic-yearCreate first academic year ({ name, start_date, end_date })
POST/api/school-setup/gradesSeed grades ({ grades: [{ name, sequence }] })

Master data

These modules share standard CRUD: GET (list), POST (create), PUT /<id> (update), DELETE /<id> (delete).

Base pathResource
/api/gradesGrades
/api/school-unitsSchool units
/api/programmesAcademic programmes
/api/mediumsMediums of instruction
/api/religionsReligions
/api/subject-contextsSubject contexts

Devices (/api/devices)

Push-notification device registration.

MethodPathPurpose
POST/api/devices/registerRegister device ({ "token": "fcm-token", "platform": "android" })
DELETE/api/devices/<token>Deregister device

Subscription (/api/subscription)

MethodPathPurpose
GET/api/subscription/statusCurrent subscription status
POST/api/subscription/upgradeUpgrade plan ({ "plan_id": 3 })