https://axion-qytr.onrender.com
All endpoints follow the pattern: POST /api/:moduleName/:fnName
1. Register → POST /api/user/register → returns longToken (superadmin)
2. Login → POST /api/user/login → returns longToken
3. Get Token → POST /api/token/v1_createShortToken (Header: token = longToken) → returns shortToken
4. API Calls → POST /api/:module/:fn (Header: token = shortToken)
| Token | Purpose | Expiry | Header Key |
|---|---|---|---|
| Long Token | Represents a user identity | 3 years | token (for step 3) |
| Short Token | Represents an active session | 1 year | token (for API calls) |
All responses follow this structure:
{
"ok": true,
"data": { ... }
}{
"ok": false,
"errors": [ "error details" ]
}{
"ok": false,
"message": "Error description"
}| HTTP Status | Meaning | When |
|---|---|---|
| 200 | Success | Request processed (check ok field) |
| 401 | Unauthorized | Missing or invalid token |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded (100 req/15min) |
| 500 | Internal Server Error | Unexpected server error |
Register a new superadmin account. This is a public endpoint — no authentication required. Multiple superadmins can be created.
Auth: None (public)
Request Body:
{
"username": "admin1",
"email": "admin1@company.com",
"password": "Secur3Pass!"
}| Field | Type | Required | Description |
|---|---|---|---|
username |
string | Yes | 3-20 characters |
email |
string | Yes | Valid email, must be unique |
password |
string | Yes | 8-100 characters |
Success Response:
{
"ok": true,
"data": {
"user": {
"_id": "65f...",
"username": "admin1",
"email": "admin1@company.com",
"role": "superadmin",
"school": null
},
"longToken": "eyJhbGciOiJI..."
}
}Error Responses:
{ "ok": false, "message": "A user with this email already exists" }
{ "ok": false, "message": "A user with this username already exists" }Authenticate any user (superadmin or school admin) and receive a long token.
Auth: None (public)
Request Body:
{
"email": "admin1@company.com",
"password": "Secur3Pass!"
}Success Response:
{
"ok": true,
"data": {
"user": {
"_id": "65f...",
"username": "admin1",
"email": "admin1@company.com",
"role": "superadmin",
"school": null
},
"longToken": "eyJhbGciOiJI..."
}
}Error Response:
{ "ok": false, "message": "Invalid email or password" }Create a school administrator account. Superadmin only. The new user is assigned to a specific school and can only manage resources within that school.
Auth: Short Token (Header: token)
Request Body:
{
"username": "schooladmin1",
"email": "admin@springfield.edu",
"password": "Secur3Pass!",
"school": "65f..."
}| Field | Type | Required | Description |
|---|---|---|---|
username |
string | Yes | 3-20 characters |
email |
string | Yes | Valid email, must be unique |
password |
string | Yes | 8-100 characters |
school |
string | Yes | ObjectId of the school to assign |
Success Response:
{
"ok": true,
"data": {
"user": {
"_id": "65f...",
"username": "schooladmin1",
"email": "admin@springfield.edu",
"role": "school_admin",
"school": "65f...",
"createdBy": "admin1"
},
"longToken": "eyJhbGciOiJI..."
}
}Exchange a long token for a short token. The short token is used for all authenticated API calls.
Auth: Long Token (Header: token)
Request Body: {}
Success Response:
{
"ok": true,
"data": {
"shortToken": "eyJhbGciOiJI..."
}
}Create a new school. Superadmin only.
Auth: Short Token
Request Body:
{
"name": "Springfield Elementary",
"address": "123 Main St, Springfield",
"phone": "+1234567890",
"email": "info@springfield.edu"
}| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | 3-100 characters |
address |
string | Yes | 3-250 characters |
phone |
string | No | Up to 20 characters |
email |
string | No | Unique school email |
Success Response:
{
"ok": true,
"data": {
"school": {
"_id": "65f...",
"name": "Springfield Elementary",
"address": "123 Main St, Springfield",
"phone": "+1234567890",
"email": "info@springfield.edu",
"createdBy": "admin1",
"updatedBy": null,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
}
}List all schools. School admins only see their assigned school.
Auth: Short Token
Request Body: {}
Success Response:
{
"ok": true,
"data": {
"schools": [
{
"_id": "65f...",
"name": "Springfield Elementary",
"address": "123 Main St, Springfield",
"phone": "+1234567890",
"email": "info@springfield.edu"
}
]
}
}Get a single school by ID.
Auth: Short Token
Request Body:
{
"id": "65f..."
}Update a school. Superadmin only.
Auth: Short Token
Request Body:
{
"id": "65f...",
"name": "New School Name",
"address": "456 New Address"
}Delete a school. Superadmin only.
Auth: Short Token
Request Body:
{
"id": "65f..."
}Success Response:
{
"ok": true,
"data": {
"message": "School deleted successfully"
}
}Create a classroom in a school.
Auth: Short Token
Access: Superadmin (any school) or School Admin (own school only)
Request Body:
{
"name": "Grade 1A",
"school": "65f...",
"capacity": 30,
"resources": ["projector", "whiteboard", "computers"]
}| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | 1-100 characters |
school |
string | Yes | School ObjectId |
capacity |
number | Yes | Min 1 |
resources |
string[] | No | List of resources |
List classrooms. School admins only see their school's classrooms.
Auth: Short Token
Request Body:
{
"school": "65f..."
}Success Response:
{
"ok": true,
"data": {
"classrooms": [
{
"_id": "65f...",
"name": "Grade 1A",
"school": { "_id": "65f...", "name": "Springfield Elementary" },
"capacity": 30,
"resources": ["projector", "whiteboard"]
}
]
}
}Get a single classroom.
Auth: Short Token
Request Body:
{
"id": "65f..."
}Update a classroom.
Auth: Short Token (Admin+ with school access)
Request Body:
{
"id": "65f...",
"name": "Grade 1B",
"capacity": 35,
"resources": ["projector", "whiteboard", "computers"]
}Delete a classroom. Fails if classroom has enrolled students.
Auth: Short Token (Admin+ with school access)
Request Body:
{
"id": "65f..."
}Enroll a new student. Checks classroom capacity before enrolling.
Auth: Short Token (Admin+ with school access)
Request Body:
{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@student.com",
"dateOfBirth": "2010-05-15",
"gender": "male",
"school": "65f...",
"classroom": "65f..."
}| Field | Type | Required | Description |
|---|---|---|---|
firstName |
string | Yes | 2-50 characters |
lastName |
string | Yes | 2-50 characters |
email |
string | Yes | Unique student email |
dateOfBirth |
string | Yes | Date string |
gender |
string | Yes | One of: male, female, non-binary |
school |
string | Yes | School ObjectId |
classroom |
string | Yes | Classroom ObjectId |
Success Response:
{
"ok": true,
"data": {
"student": {
"_id": "65f...",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@student.com",
"dateOfBirth": "2010-05-15",
"gender": "male",
"school": "65f...",
"classroom": "65f...",
"enrollmentDate": "2024-01-01T00:00:00.000Z",
"status": "enrolled",
"createdBy": "schooladmin1",
"updatedBy": null
}
}
}List students with optional filters.
Auth: Short Token
Request Body:
{
"school": "65f...",
"classroom": "65f...",
"status": "enrolled"
}All filter fields are optional.
Get a single student by ID.
Auth: Short Token
Request Body:
{
"id": "65f..."
}Update student profile information.
Auth: Short Token (Admin+ with school access)
Request Body:
{
"id": "65f...",
"firstName": "Jonathan",
"lastName": "Doe"
}Remove a student.
Auth: Short Token (Admin+ with school access)
Request Body:
{
"id": "65f..."
}Transfer a student to a different classroom and/or school. Checks capacity of the destination classroom.
Auth: Short Token (Admin+ with access to source school)
Request Body (same school — move to different classroom):
{
"studentId": "65f...",
"newClassroomId": "65f..."
}Request Body (different school — requires new classroom):
{
"studentId": "65f...",
"newSchoolId": "65f...",
"newClassroomId": "65f..."
}| Field | Type | Required | Description |
|---|---|---|---|
studentId |
string | Yes | Student to transfer |
newSchoolId |
string | No | Destination school (if changing schools) |
newClassroomId |
string | Yes* | Destination classroom (* required when changing school) |
Success Response:
{
"ok": true,
"data": {
"message": "Student transferred successfully",
"student": {
"_id": "65f...",
"firstName": "John",
"lastName": "Doe",
"school": { "_id": "65f...", "name": "New School" },
"classroom": { "_id": "65f...", "name": "Grade 2A" },
"status": "transferred"
}
}
}┌──────────────────┐ ┌──────────────────┐
│ School │ │ User │
├──────────────────┤ ├──────────────────┤
│ _id: ObjectId │◄────────│ school: ObjectId │
│ name: String │ │ _id: ObjectId │
│ address: String │ │ username: String │
│ phone: String │ │ email: String │
│ email: String │ │ password: String │
│ createdBy: String│ │ role: String │
│ updatedBy: String│ │ createdBy: String│
│ createdAt: Date │ │ createdAt: Date │
│ updatedAt: Date │ │ updatedAt: Date │
└────────┬─────────┘ └──────────────────┘
│
│ 1:N
▼
┌──────────────────┐ ┌──────────────────┐
│ Classroom │ │ Student │
├──────────────────┤ ├──────────────────┤
│ _id: ObjectId │◄────────│ classroom: ObjId │
│ school: ObjectId │ │ _id: ObjectId │
│ name: String │────────►│ school: ObjectId │
│ capacity: Number │ │ firstName: String│
│ resources: [Str] │ │ lastName: String │
│ createdBy: String│ │ email: String │
│ updatedBy: String│ │ dateOfBirth: Str │
│ createdAt: Date │ │ gender: String │
│ updatedAt: Date │ │ enrollmentDate │
└──────────────────┘ │ status: String │
│ createdBy: String│
│ updatedBy: String│
│ createdAt: Date │
│ updatedAt: Date │
└──────────────────┘
- Limit: 100 requests per 15-minute window per IP address
- Response when exceeded:
{
"ok": false,
"errors": "Too many requests, please try again later."
}- Passwords hashed with bcrypt (10 salt rounds)
- JWT tokens for stateless authentication
- Helmet security headers
- CORS enabled
- Rate limiting on all API routes
- Input validation on all endpoints
- Role-based access preventing unauthorized resource access
- School-scoping ensuring school admins can only access their school's data