> ## Documentation Index
> Fetch the complete documentation index at: https://docs.criar.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication Module

> SKOLE-AUTH — Authentication Module

## 1. Overview

| Field              | Value                                                                                                                                                                                                                     |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Module Name**    | Authentication                                                                                                                                                                                                            |
| **Module Code**    | AUTH                                                                                                                                                                                                                      |
| **Product**        | SKOLE (All surfaces)                                                                                                                                                                                                      |
| **Version**        | 1.0                                                                                                                                                                                                                       |
| **Status**         | Active                                                                                                                                                                                                                    |
| **Business Value** | Secures all platform data behind role-appropriate authentication. Each user type (parent, teacher/staff, admin) has a dedicated auth flow. Session tokens with FCM device registration ensure push-notification delivery. |

### Scope In

* Parent login via phone number + PIN
* Teacher/Staff login via phone + PIN
* Web Admin login via email + password
* Session/device management
* Logout for all roles
* PIN setup for first-time parents

### Scope Out

* OAuth / Social login
* Two-factor authentication (schema reserved, not active)
* Password reset via OTP (available endpoint, not wired to SMS)

***

## 2. Requirements

### Functional Requirements (FR)

*What the module must DO — actions, behaviors, and outcomes.*

* **SKOLE-AUTH-FR001:** The module shall allow parents to authenticate using their registered mobile number and a PIN.
* **SKOLE-AUTH-FR002:** The module shall match both father and mother phone numbers against `parent_details`.
* **SKOLE-AUTH-FR003:** The module shall return all linked children's profiles if the PIN matches.
* **SKOLE-AUTH-FR004:** The module shall create a `parent_devices` record with device info and FCM token on login.
* **SKOLE-AUTH-FR005:** The module shall generate and return a JWT containing `sub`, `type`, `skole_id`, `phone`, and `session_token`.
* **SKOLE-AUTH-FR006:** The module shall allow teachers to authenticate with phone number and PIN against the `staff` table.
* **SKOLE-AUTH-FR007:** The module shall create a `staff_sessions` record on teacher login.
* **SKOLE-AUTH-FR008:** The module shall allow web admins to sign up with name, email, phone, and password.
* **SKOLE-AUTH-FR009:** The module shall allow web admins to sign in with email and password.
* **SKOLE-AUTH-FR010:** The module shall invalidate sessions on logout by marking the record as inactive (`is_active = 0`).
* **SKOLE-AUTH-FR011:** The module shall allow updating device info (FCM token, platform, model) post-login.
* **SKOLE-AUTH-FR012:** The module shall provide a PIN setup flow for first-time parent users.
* **SKOLE-AUTH-FR013:** The module shall allow staff to set or edit passwords separately from the parent flow.

### Non-Functional Requirements (NFR)

*How well the module must do it — performance, security, and reliability.*

* **SKOLE-AUTH-NFR001:** The module shall perform authentication checks ensuring JWT tokens are short-lived and sessions expire after 30 days.
* **SKOLE-AUTH-NFR002:** The module shall behave securely by ensuring session tokens are unique per device.
* **SKOLE-AUTH-NFR003:** The module shall behave as a secure gateway by protecting all non-auth routes with a global `JwtAuthGuard`.
* **SKOLE-AUTH-NFR004:** The module shall perform bypass logic for auth routes decorated with `@Public()`.
* **SKOLE-AUTH-NFR005:** The module shall support JWT extraction via Authorization header, query parameter, or cookie.

### Constraints

*Rules and boundaries — tech choices and platform restrictions.*

* **C001:** We must use PostgreSQL (via Prisma) for persisting users and sessions because it is the platform's primary data store.
* **C002:** We must use Firebase FCM for push notification registration to ensure cross-platform delivery.
* **C003:** We must use standard JWT (`jsonwebtoken`) for token signing because the architecture requires stateless authentication.

***

## 3. Sub-modules / Backlog

| ID                 | Sub-module                  | Priority | Status | Estimate | Linked Requirement |
| ------------------ | --------------------------- | -------- | ------ | -------- | ------------------ |
| `SKOLE-AUTH-SM001` | Parent PIN Authentication   | P0       | Done   | 3d       | FR001–FR005        |
| `SKOLE-AUTH-SM002` | Teacher PIN Authentication  | P0       | Done   | 2d       | FR006–FR007        |
| `SKOLE-AUTH-SM003` | Web Admin Signup / Signin   | P0       | Done   | 2d       | FR008–FR009        |
| `SKOLE-AUTH-SM004` | Session & Device Management | P1       | Done   | 2d       | FR010–FR011        |
| `SKOLE-AUTH-SM005` | PIN Setup for New Parents   | P1       | Done   | 1d       | FR012              |
| `SKOLE-AUTH-SM006` | Staff Password Management   | P1       | Done   | 1d       | FR013              |

***

## 4. Logical Implementation

### Parent Authentication Flow

```text theme={null}
POST /parent-app/parent-auth-verification
  ├─ Input: { phone_no, pin, fcm_token, device_info }
  ├─ Step 1: Query parent_details WHERE father_phone = phone OR mother_phone = phone
  │    └─ If no record → 401 "Invalid phone number"
  ├─ Step 2: Filter records WHERE pin matches
  │    └─ If no match → 401 "Invalid PIN"
  ├─ Step 3: Collect validParentRecords = pin matches + null-PIN siblings
  ├─ Step 4: Query students table for all linked children
  ├─ Step 5: Generate session_token (UUID) + expiresAt (now + 30 days)
  ├─ Step 6: Insert parent_devices record
  ├─ Step 7: Update parent_details.fcm_token
  ├─ Step 8: Sign JWT payload → return access_token + parent profile + children[]
```

### Teacher Authentication Flow

```text theme={null}
POST /teachers-app/teacher-auth-verification
  ├─ Input: { phone_no, pin, fcm_token, device_info }
  ├─ Step 1: Query staff WHERE phone_no = input AND skole_id = input.skole_id
  │    └─ If no record → 401
  ├─ Step 2: Verify PIN match
  ├─ Step 3: Insert staff_sessions record
  ├─ Step 4: Sign JWT with type='staff'
  └─ Return access_token + staff profile
```

### Web Admin Signin Flow

```text theme={null}
POST /web-app/signin
  ├─ Input: { email, password }
  ├─ Step 1: Query users WHERE email = input
  │    └─ If not found → 401
  ├─ Step 2: bcrypt.compare(password, user.password)
  ├─ Step 3: Insert user_sessions record
  ├─ Step 4: Sign JWT with type='user'
  └─ Return access_token + user profile
```

### JWT Guard Strategy

All API routes are protected globally. The `@Public()` decorator (custom metadata key `isPublic`) bypasses the guard. JWT is extracted from:

1. `Authorization: Bearer <token>` header
2. `?access_token=<token>` query parameter
3. Cookie `access_token`

### Error Handling

| Scenario                     | HTTP Code | Message                |
| ---------------------------- | --------- | ---------------------- |
| Phone not found              | 401       | "Invalid phone number" |
| Wrong PIN                    | 401       | "Invalid PIN"          |
| Expired session              | 401       | "Unauthorized"         |
| Missing roll\_no / skole\_id | 400       | Field-specific message |

***

## 5. UI Requirements

### Parent App — Auth Screens

| ID                 | Screen               | Route              | Description                                                    |
| ------------------ | -------------------- | ------------------ | -------------------------------------------------------------- |
| `SKOLE-AUTH-UI001` | PinLoginScreen       | `/pin-login`       | Phone number and 4-digit PIN entry for parents                 |
| `SKOLE-AUTH-UI002` | ChildSelectionScreen | `/child-selection` | Screen to select which child to view (for multi-child parents) |

### Teacher App — Auth Screens

| ID                 | Screen      | Route    | Description                             |
| ------------------ | ----------- | -------- | --------------------------------------- |
| `SKOLE-AUTH-UI006` | LoginScreen | `/login` | Phone + PIN single-page login for staff |

### Web App — Auth Screen

| ID                 | Screen     | Route    | Description                              |
| ------------------ | ---------- | -------- | ---------------------------------------- |
| `SKOLE-AUTH-UI007` | Login Page | `/login` | Email + password login for school admins |

***

### UI Components

| ID                 | Component         | Props                              | Used In             |
| ------------------ | ----------------- | ---------------------------------- | ------------------- |
| `SKOLE-AUTH-UC001` | `PhoneInputField` | `value`, `onChange`, `placeholder` | UI001, UI002, UI006 |
| `SKOLE-AUTH-UC002` | `PinInput`        | `length=4`, `onComplete(pin)`      | UI003, UI004        |
| `SKOLE-AUTH-UC003` | `AuthButton`      | `label`, `onPress`, `loading`      | All auth screens    |
| `SKOLE-AUTH-UC004` | `SchoolLogo`      | `skole_id`                         | UI001, UI006        |

### UI States

| Screen         | Loading                   | Empty / First Time                 | Error                        |
| -------------- | ------------------------- | ---------------------------------- | ---------------------------- |
| PinLoginScreen | Spinner overlay on submit | Redirect to PinSetup if no PIN set | "Invalid PIN" toast          |
| LoginScreen    | Disabled button + spinner | —                                  | "Invalid phone number" toast |
| Web Login      | Button spinner            | —                                  | Inline error message         |

***

## 6. Conditional Expressions

| ID                 | Expression                       | Trigger                       | True Action                        | False Action             |
| ------------------ | -------------------------------- | ----------------------------- | ---------------------------------- | ------------------------ |
| `SKOLE-AUTH-CE001` | `allParentRecords.length === 0`  | POST parent-auth-verification | Return 401 "Invalid phone number"  | Proceed to PIN check     |
| `SKOLE-AUTH-CE002` | `pinMatches.length === 0`        | After phone lookup            | Return 401 "Invalid PIN"           | Build validParentRecords |
| `SKOLE-AUTH-CE003` | `p.pin === null`                 | Building validParentRecords   | Include sibling records (null PIN) | Exclude                  |
| `SKOLE-AUTH-CE004` | `@Public()` decorator present    | Every API request             | Skip JwtAuthGuard                  | Enforce JWT check        |
| `SKOLE-AUTH-CE005` | `user.pin === null` (parent app) | PinLoginScreen render         | Redirect to PinSetupScreen         | Show PIN entry           |
| `SKOLE-AUTH-CE006` | `jwt.type === 'parent'`          | JWT validation in guard       | Attach parent context              | Check other type         |
| `SKOLE-AUTH-CE007` | `jwt.type === 'staff'`           | JWT validation                | Attach staff context               | Check other type         |
| `SKOLE-AUTH-CE008` | `session.is_active === 0`        | Any authenticated request     | 401 Unauthorized                   | Allow request            |

***

## 7. Internal Module Connections

| Direction          | Module             | Data / Event                                                 | Condition                        |
| ------------------ | ------------------ | ------------------------------------------------------------ | -------------------------------- |
| AUTH → STUD        | Student Management | Children list returned on login                              | Always on successful parent auth |
| AUTH → PROF        | Profile            | Parent profile data embedded in auth response                | Always                           |
| AUTH → NOTF        | Notifications      | FCM token stored in parent\_devices / staff record           | On login / device update         |
| STUD → AUTH        | Student Management | `roll_no`, `skole_id` used to scope auth                     | Always                           |
| All modules → AUTH | Every module       | JWT `session_token`, `skole_id`, `sub` used in every request | All protected routes             |

***

## 8. External Connections

| ID                 | Service                 | Purpose                                  | Auth Method                           | Failure Behavior                           |
| ------------------ | ----------------------- | ---------------------------------------- | ------------------------------------- | ------------------------------------------ |
| `SKOLE-AUTH-EX001` | PostgreSQL (via Prisma) | Persist users, sessions, parent\_details | DB credentials via `DATABASE_URL` env | 500 Internal Server Error                  |
| `SKOLE-AUTH-EX002` | Firebase FCM            | Push notification token registration     | Firebase Admin SDK (server key)       | Auth succeeds, notifications silently fail |
| `SKOLE-AUTH-EX003` | JWT (jsonwebtoken)      | Token signing / verification             | `JWT_SECRET` env variable             | 401 Unauthorized                           |

***

## 9. Database Tables

| ID                 | Table            | Role                                                 |
| ------------------ | ---------------- | ---------------------------------------------------- |
| `SKOLE-AUTH-TB001` | `users`          | Web admin accounts                                   |
| `SKOLE-AUTH-TB002` | `parent_details` | Parent phone, PIN, email, FCM token                  |
| `SKOLE-AUTH-TB003` | `parent_devices` | Device sessions for parents (unique `session_token`) |
| `SKOLE-AUTH-TB004` | `staff`          | Staff records including PIN and FCM token            |
| `SKOLE-AUTH-TB005` | `staff_sessions` | Device sessions for staff (unique `session_token`)   |
| `SKOLE-AUTH-TB006` | `user_sessions`  | Device sessions for web admins                       |

### Key Relationships

* `parent_devices.parent_id` → `parent_details.id`
* `parent_details.student_roll_no` + `parent_details.skole_id` → `students.roll_no` + `students.skole_id`
* `staff_sessions.staff_id` → `staff.id`
* `user_sessions.user_id` → `users.id`

***

## 10. API Endpoints Summary

| ID                 | Method | Route                                     | Auth         | Description                 |
| ------------------ | ------ | ----------------------------------------- | ------------ | --------------------------- |
| `SKOLE-AUTH-EP001` | POST   | `/parent-app/parent-auth-verification`    | Public       | Parent phone+PIN login      |
| `SKOLE-AUTH-EP002` | POST   | `/parent-app/logout`                      | JWT (parent) | Invalidate parent session   |
| `SKOLE-AUTH-EP003` | PUT    | `/parent-app/auth/device-info`            | JWT (parent) | Update device/FCM info      |
| `SKOLE-AUTH-EP004` | POST   | `/teachers-app/teacher-auth-verification` | Public       | Teacher phone+PIN login     |
| `SKOLE-AUTH-EP005` | POST   | `/teachers-app/logout`                    | JWT (staff)  | Invalidate staff session    |
| `SKOLE-AUTH-EP006` | POST   | `/web-app/signup`                         | Public       | Create admin account        |
| `SKOLE-AUTH-EP007` | POST   | `/web-app/signin`                         | Public       | Admin email+password login  |
| `SKOLE-AUTH-EP008` | POST   | `/web-app/reset-password`                 | Public       | Reset admin password        |
| `SKOLE-AUTH-EP009` | POST   | `/web-app/parent-set-password`            | Public       | Set parent PIN (first time) |
| `SKOLE-AUTH-EP010` | PATCH  | `/web-app/parent-edit-password`           | Public       | Edit parent PIN             |
| `SKOLE-AUTH-EP011` | POST   | `/web-app/staff-set-password`             | Public       | Set staff PIN (first time)  |
| `SKOLE-AUTH-EP012` | PATCH  | `/web-app/staff-edit-password`            | Public       | Edit staff PIN              |
| `SKOLE-AUTH-EP013` | POST   | `/web-app/logout`                         | JWT (user)   | Invalidate admin session    |

***

* Old devices can be logged out via separate endpoint

### EP001: Parent Phone+PIN Login

#### Section 1: Endpoint Summary

Authenticates parent users using their registered phone number and PIN. Returns JWT token, parent profile, and all linked children for multi-child families.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/parent-app/parent-auth-verification`
* **Content-Type:** `application/json`
* **Authentication:** Public (no token required)
* **Rate Limit:** 5 requests per minute per IP

#### Section 3: Path Parameters

None required

#### Section 4: Query Parameters

None required

#### Section 5: Request Body Schema

```json theme={null}
{
  "phone_no*": "String (10-15 digits, with or without country code)",
  "pin*": "String (4-6 digit PIN)",
  "fcm_token": "String (Firebase Cloud Messaging device token)",
  "device_info": {
    "platform": "String (ios, android, web)",
    "model": "String (device model name)",
    "os_version": "String (operating system version)"
  }
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Authentication successful",
  "data": {
    "access_token": "JWT token string",
    "session_token": "UUID (unique per device)",
    "expires_in": "2592000 (seconds, 30 days)",
    "token_type": "Bearer",
    "parent": {
      "id": "UUID",
      "skole_id": "UUID",
      "father_phone": "String",
      "mother_phone": "String (nullable)",
      "email": "String (nullable)",
      "pin": "****",
      "father_name": "String",
      "mother_name": "String (nullable)",
      "fcm_token": "String (updated)"
    },
    "children": [
      {
        "id": "UUID",
        "skole_id": "UUID",
        "first_name": "String",
        "last_name": "String",
        "roll_no": "Integer",
        "class": "String",
        "section": "String",
        "status": "active"
      }
    ]
  }
}
```

#### Section 7: Error Responses

**400 - Bad Request**

```json theme={null}
{
  "status": "error",
  "code": "VALIDATION_ERROR",
  "message": "Validation failed",
  "errors": [
    {"field": "phone_no", "message": "phone_no is required"},
    {"field": "pin", "message": "PIN must be 4-6 digits"}
  ]
}
```

**401 - Invalid Phone**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_PHONE",
  "message": "Phone number not registered",
  "timestamp": "2026-03-16T10:30:45.000Z"
}
```

**401 - Invalid PIN**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_PIN",
  "message": "Incorrect PIN",
  "timestamp": "2026-03-16T10:30:45.000Z"
}
```

**429 - Rate Limited**

```json theme={null}
{
  "status": "error",
  "code": "RATE_LIMITED",
  "message": "Too many login attempts. Try again in 1 minute",
  "retry_after": 60
}
```

#### Section 8: Implementation Examples

**JavaScript (Node.js/React Native):**

```javascript theme={null}
const loginParent = async (phoneNo, pin, fcmToken) => {
  try {
    const response = await fetch('https://api.skole.com/v1/parent-app/parent-auth-verification', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        phone_no: phoneNo,
        pin: pin,
        fcm_token: fcmToken,
        device_info: {
          platform: 'android',
          model: 'samsung-galaxy-a12',
          os_version: '11.0'
        }
      })
    });

    const data = await response.json();
    
    if (data.status === 'success') {
      const { access_token, session_token, children } = data.data;
      
      // Store token securely (AsyncStorage for React Native)
      await AsyncStorage.setItem('authToken', access_token);
      await AsyncStorage.setItem('sessionToken', session_token);
      
      console.log(`Logged in. Found ${children.length} children`);
      return data.data;
    } else {
      console.error('Login failed:', data.message);
      throw new Error(data.message);
    }
  } catch (error) {
    console.error('Network error:', error);
    throw error;
  }
};

// Usage
await loginParent('9876543210', '1234', 'fcm_token_here');
```

**Python:**

```python theme={null}
import requests
import json
import asyncio

async def login_parent(phone_no: str, pin: str, fcm_token: str):
    url = 'https://api.skole.com/v1/parent-app/parent-auth-verification'
    
    payload = {
        'phone_no': phone_no,
        'pin': pin,
        'fcm_token': fcm_token,
        'device_info': {
            'platform': 'ios',
            'model': 'iphone-13',
            'os_version': '15.0'
        }
    }
    
    headers = {'Content-Type': 'application/json'}
    
    try:
        response = requests.post(url, json=payload, headers=headers, timeout=10)
        data = response.json()
        
        if data['status'] == 'success':
            token = data['data']['access_token']
            children = data['data']['children']
            print(f"Logged in successfully. Children: {len(children)}")
            return data['data']
        else:
            raise Exception(f"Login failed: {data['message']}")
    except requests.exceptions.RequestException as e:
        print(f"Network error: {e}")
        raise

# Usage
asyncio.run(login_parent('9876543210', '1234', 'fcm_token_here'))
```

**cURL:**

```bash theme={null}
curl -X POST \
  'https://api.skole.com/v1/parent-app/parent-auth-verification' \
  -H 'Content-Type: application/json' \
  -d '{
    "phone_no": "9876543210",
    "pin": "1234",
    "fcm_token": "eZuB8bKWO3c:APA91bH...",
    "device_info": {
      "platform": "android",
      "model": "samsung-a12",
      "os_version": "11.0"
    }
  }'
```

#### Section 9: Database Context

**Primary Table:** `parent_details`

* **Primary Key:** `id` (UUID)
* **School Isolation:** `skole_id`

**Query Flow:**

1. SELECT \* FROM parent\_details WHERE (father\_phone = ? OR mother\_phone = ?) AND skole\_id = ?
2. Verify: password.verify(pin, parent.pin\_hash)
3. SELECT \* FROM students WHERE parent\_id = ? AND status = 'active'
4. INSERT INTO parent\_devices (parent\_id, session\_token, fcm\_token, device\_info, created\_at)

**Indexed Fields:**

* father\_phone (for query performance)
* mother\_phone (for query performance)
* skole\_id (for school isolation)
* session\_token (for session lookup)

**Data Retention:** Keep parent\_devices for 2 years, archive afterward

#### Section 10: Business Logic & Validations

**Validation Rules:**

* phone\_no: 10-15 digits, with or without country code, must exist in parent\_details
* pin: 4-6 digits, bcrypt hashed in database
* skole\_id: Must match user's school
* device\_info: Optional but recommended

**Authorization:**

* No prior authentication required (public endpoint)
* After login, JWT validates on all subsequent requests
* Parent can only access their own children and school data

**Business Logic:**

1. Search for parent by father\_phone or mother\_phone
2. If found, verify PIN against bcrypt hash
3. If PIN matches, collect all active children linked to parent
4. Generate unique session\_token (UUID) with 30-day expiry
5. Create parent\_devices record for multi-device tracking
6. Update FCM token for push notifications
7. Sign JWT with parent type and return access\_token

**Special Behaviors:**

* PIN is never returned in response (masked as \*\*\*\*)
* If parent has no PIN set, redirect to PIN setup flow
* Multiple devices supported per parent (multi-login)
* Old devices can be logged out via separate endpoint

#### Section 11: Related Endpoints

* **PUT** `/parent-app/auth/device-info` - Update device FCM token
* **POST** `/parent-app/logout` - Logout and invalidate session
* **POST** `/web-app/parent-set-password` - Setup PIN for first time
* **PATCH** `/web-app/parent-edit-password` - Change existing PIN
* **GET** `/parent-app/student` - Get children after login

#### Section 12: Response Summary Table

| Status | Scenario        | Error Code        | Message                     |
| ------ | --------------- | ----------------- | --------------------------- |
| 200 ✅  | Success         | -                 | Authentication successful   |
| 400 ❌  | Missing fields  | VALIDATION\_ERROR | Validation failed           |
| 401 ❌  | Phone not found | INVALID\_PHONE    | Phone number not registered |
| 401 ❌  | Wrong PIN       | INVALID\_PIN      | Incorrect PIN               |
| 429 ❌  | Rate limit      | RATE\_LIMITED     | Too many login attempts     |
| 500 ❌  | Server error    | INTERNAL\_ERROR   | Server error occurred       |

***

### EP002: Parent Logout

#### Section 1: Endpoint Summary

Invalidates parent session by marking the device session as inactive, effectively logging out the parent from that specific device.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/parent-app/logout`
* **Content-Type:** `application/json`
* **Authentication:** JWT Bearer Token (Required - Parent)
* **Rate Limit:** Unlimited

#### Section 3: Path Parameters

None

#### Section 4: Query Parameters

| Parameter | Type | Required | Default  | Description                           | Example                                |
| --------- | ---- | -------- | -------- | ------------------------------------- | -------------------------------------- |
| skole\_id | UUID | No       | From JWT | School identifier (optional override) | `550e8400-e29b-41d4-a716-446655440000` |

#### Section 5: Request Body Schema

```json theme={null}
{
  "logout_all_devices": "Boolean (optional, defaults to false)"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Logged out successfully",
  "data": {
    "logged_out_devices": "Integer (1 if logout_all_devices=false, count if true)",
    "timestamp": "2026-03-16T10:30:45.000Z"
  }
}
```

#### Section 7: Error Responses

**401 - Unauthorized**

```json theme={null}
{
  "status": "error",
  "code": "UNAUTHORIZED",
  "message": "Authorization header missing or invalid"
}
```

**404 - Session Not Found**

```json theme={null}
{
  "status": "error",
  "code": "SESSION_NOT_FOUND",
  "message": "Active session not found"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const logoutParent = async (token, logoutAllDevices = false) => {
  try {
    const response = await fetch('https://api.skole.com/v1/parent-app/logout', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        logout_all_devices: logoutAllDevices
      })
    });

    const data = await response.json();
    
    if (data.status === 'success') {
      await AsyncStorage.removeItem('authToken');
      console.log('Logged out successfully');
      return true;
    } else {
      console.error('Logout failed:', data.message);
      return false;
    }
  } catch (error) {
    console.error('Network error:', error);
    throw error;
  }
};

// Usage
await logoutParent(token, false); // Logout from current device
await logoutParent(token, true);  // Logout from all devices
```

**Python:**

```python theme={null}
def logout_parent(token: str, logout_all_devices: bool = False) -> bool:
    url = 'https://api.skole.com/v1/parent-app/logout'
    headers = {
        'Authorization': f'Bearer {token}',
        'Content-Type': 'application/json'
    }
    data = {'logout_all_devices': logout_all_devices}
    
    try:
        response = requests.post(url, json=data, headers=headers, timeout=10)
        result = response.json()
        
        if result['status'] == 'success':
            print('Logout successful')
            return True
        else:
            print(f"Logout failed: {result['message']}")
            return False
    except requests.exceptions.RequestException as e:
        print(f"Network error: {e}")
        return False

# Usage
logout_parent(token, logout_all_devices=True)
```

**cURL:**

```bash theme={null}
curl -X POST \
  'https://api.skole.com/v1/parent-app/logout' \
  -H 'Authorization: Bearer YOUR_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{"logout_all_devices": false}'
```

#### Section 9: Database Context

**Tables Affected:**

* `parent_devices` - Mark is\_active = 0
* Query: UPDATE parent\_devices SET is\_active = 0 WHERE session\_token = ? AND parent\_id = ?

**Behavior:**

* If logout\_all\_devices = false: Only invalidate current session\_token
* If logout\_all\_devices = true: Invalidate all session\_tokens for the parent

#### Section 10: Business Logic & Validations

**Validation:**

* JWT must be valid and not expired
* Parent must have an active session
* session\_token must exist in parent\_devices

**Business Logic:**

1. Extract session\_token from JWT
2. Find parent\_devices record where session\_token matches
3. Mark is\_active = 0
4. If logout\_all\_devices = true, also mark all other devices as inactive
5. Return success message

#### Section 11: Related Endpoints

* **POST** `/parent-app/parent-auth-verification` - Login again
* **PUT** `/parent-app/auth/device-info` - Manage device tokens
* **GET** `/parent-app/profile` - Get parent profile after login

#### Section 12: Response Summary Table

| Status | Scenario          | Error Code          | Message                                 |
| ------ | ----------------- | ------------------- | --------------------------------------- |
| 200 ✅  | Success           | -                   | Logged out successfully                 |
| 401 ❌  | Invalid token     | UNAUTHORIZED        | Authorization header missing or invalid |
| 404 ❌  | Session not found | SESSION\_NOT\_FOUND | Active session not found                |
| 500 ❌  | Database error    | INTERNAL\_ERROR     | Server error occurred                   |

***

### EP003: Update Parent Device Info

#### Section 1: Endpoint Summary

Updates device information (FCM token, platform, model) for push notification delivery without requiring re-login.

#### Section 2: HTTP Details

* **HTTP Method:** PUT
* **Endpoint URL:** `/parent-app/auth/device-info`
* **Content-Type:** `application/json`
* **Authentication:** JWT Bearer Token (Required - Parent)
* **Rate Limit:** 100 requests per minute

#### Section 3: Path Parameters

None

#### Section 4: Query Parameters

None

#### Section 5: Request Body Schema

```json theme={null}
{
  "fcm_token": "String (Firebase Cloud Messaging token)",
  "platform": "Enum: ios, android, web",
  "model": "String (device model name)",
  "os_version": "String (operating system version)"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Device information updated",
  "data": {
    "device_id": "UUID",
    "fcm_token": "Updated token string",
    "platform": "ios",
    "model": "iphone-13-pro",
    "os_version": "15.2",
    "updated_at": "2026-03-16T10:30:45.000Z"
  }
}
```

#### Section 7: Error Responses

**400 - Invalid FCM Token**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_FCM_TOKEN",
  "message": "FCM token format is invalid or expired"
}
```

**401 - Unauthorized**

```json theme={null}
{
  "status": "error",
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired JWT token"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const updateDeviceInfo = async (token, fcmToken) => {
  try {
    const response = await fetch('https://api.skole.com/v1/parent-app/auth/device-info', {
      method: 'PUT',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        fcm_token: fcmToken,
        platform: 'ios',
        model: 'iphone-13-pro',
        os_version: '15.2'
      })
    });

    const data = await response.json();
    if (data.status === 'success') {
      console.log('Device info updated');
      return true;
    } else {
      console.error('Update failed:', data.message);
      return false;
    }
  } catch (error) {
    console.error('Error:', error);
    return false;
  }
};
```

**Python:**

```python theme={null}
def update_device_info(token: str, fcm_token: str, platform: str) -> bool:
    url = 'https://api.skole.com/v1/parent-app/auth/device-info'
    headers = {
        'Authorization': f'Bearer {token}',
        'Content-Type': 'application/json'
    }
    data = {
        'fcm_token': fcm_token,
        'platform': platform,
        'model': 'samsung-galaxy-a12',
        'os_version': '11.0'
    }
    
    response = requests.put(url, json=data, headers=headers, timeout=10)
    result = response.json()
    return result['status'] == 'success'
```

**cURL:**

```bash theme={null}
curl -X PUT \
  'https://api.skole.com/v1/parent-app/auth/device-info' \
  -H 'Authorization: Bearer YOUR_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "fcm_token": "eZuB8bKWO3c:APA91bH...",
    "platform": "android",
    "model": "samsung-a12",
    "os_version": "11.0"
  }'
```

#### Section 9: Database Context

**Table:** `parent_devices` **Update Query:** UPDATE parent\_devices SET fcm\_token = ?, platform = ?, model = ?, os\_version = ?, updated\_at = NOW() WHERE session\_token = ?

#### Section 10: Business Logic & Validations

**Validation:**

* FCM token must be valid format
* Platform must be one of: ios, android, web
* JWT must be valid

**Business Logic:**

1. Extract session\_token from JWT
2. Find parent\_devices record
3. Update fcm\_token, platform, model, os\_version
4. Return updated device information

#### Section 11: Related Endpoints

* **POST** `/parent-app/logout` - Logout device
* **POST** `/parent-app/parent-auth-verification` - Initial login

#### Section 12: Response Summary Table

| Status | Scenario     | Error Code          | Message                    |
| ------ | ------------ | ------------------- | -------------------------- |
| 200 ✅  | Success      | -                   | Device information updated |
| 400 ❌  | Invalid FCM  | INVALID\_FCM\_TOKEN | FCM token format invalid   |
| 401 ❌  | Unauthorized | UNAUTHORIZED        | Invalid or expired JWT     |
| 500 ❌  | Server error | INTERNAL\_ERROR     | Server error occurred      |

***

## 12. Summary Table: All 13 Authentication Endpoints

| ID    | Endpoint                                  | Method | Auth   | Description        | Status              |
| ----- | ----------------------------------------- | ------ | ------ | ------------------ | ------------------- |
| EP001 | `/parent-app/parent-auth-verification`    | POST   | Public | Parent login       | ✅ Documented        |
| EP002 | `/parent-app/logout`                      | POST   | JWT    | Parent logout      | ✅ Documented        |
| EP003 | `/parent-app/auth/device-info`            | PUT    | JWT    | Update device info | ✅ Documented        |
| EP004 | `/teachers-app/teacher-auth-verification` | POST   | Public | Teacher login      | ⏳ See EP001 pattern |
| EP005 | `/teachers-app/logout`                    | POST   | JWT    | Teacher logout     | ⏳ See EP002 pattern |
| EP006 | `/web-app/signup`                         | POST   | Public | Admin signup       | ⏳ Coming            |
| EP007 | `/web-app/signin`                         | POST   | Public | Admin login        | ⏳ Coming            |
| EP008 | `/web-app/reset-password`                 | POST   | Public | Password reset     | ⏳ Coming            |
| EP009 | `/web-app/parent-set-password`            | POST   | Public | Setup parent PIN   | ⏳ Coming            |
| EP010 | `/web-app/parent-edit-password`           | PATCH  | Public | Change parent PIN  | ⏳ Coming            |
| EP011 | `/web-app/staff-set-password`             | POST   | Public | Setup staff PIN    | ⏳ Coming            |
| EP012 | `/web-app/staff-edit-password`            | PATCH  | Public | Change staff PIN   | ⏳ Coming            |
| EP013 | `/web-app/logout`                         | POST   | JWT    | Admin logout       | ✅ Documented        |

***

### EP004: Teacher Phone+PIN Login

#### Section 1: Endpoint Summary

Authenticates teacher/staff users using registered phone number and PIN. Returns JWT token and staff profile with assigned classes.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/teachers-app/teacher-auth-verification`
* **Content-Type:** `application/json`
* **Authentication:** Public
* **Rate Limit:** 5 requests per minute per IP

#### Section 5: Request Body Schema

```json theme={null}
{
  "phone_no*": "String (10-15 digits)",
  "pin*": "String (4-6 digit PIN)",
  "fcm_token": "String (Firebase token)"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Authentication successful",
  "data": {
    "access_token": "JWT token",
    "session_token": "UUID",
    "expires_in": 2592000,
    "token_type": "Bearer",
    "staff": {
      "id": "UUID",
      "skole_id": "UUID",
      "phone_no": "9876543210",
      "email": "teacher@school.com",
      "first_name": "Vikram",
      "last_name": "Kumar",
      "designation": "Teacher",
      "department": "Science",
      "pin": "****"
    },
    "assigned_classes": [
      {
        "class_id": "UUID",
        "class_name": "10-A",
        "subject": "Biology"
      }
    ]
  }
}
```

#### Section 7: Error Responses

**401 - Phone/PIN Invalid**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_CREDENTIALS",
  "message": "Phone number or PIN is incorrect"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const loginTeacher = async (phoneNo, pin, fcmToken) => {
  const response = await fetch('https://api.skole.com/v1/teachers-app/teacher-auth-verification', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      phone_no: phoneNo,
      pin: pin,
      fcm_token: fcmToken
    })
  });

  const data = await response.json();
  if (data.status === 'success') {
    await AsyncStorage.setItem('teacherToken', data.data.access_token);
    console.log(`Teacher ${data.data.staff.first_name} logged in`);
    return data.data;
  }
  throw new Error(data.message);
};
```

#### Section 9: Database Context

**Tables:**

* `staff_details` - Teacher/admin staff records
* `staff_class_assignments` - Assigned classes

**Indices:**

* phone\_no (for lookup)
* skole\_id (for school isolation)

#### Section 10: Business Logic & Validations

**Validation:**

* phone\_no must exist in staff\_details
* pin must match bcrypt hash
* Staff must be active status
* Must belong to correct school (skole\_id)

**Business Logic:**

1. Find staff by phone\_no and skole\_id
2. Verify PIN
3. Load all assigned classes
4. Generate JWT with staff role
5. Create staff\_devices session record
6. Return token and profile

#### Section 11: Related Endpoints

* **POST** `/teachers-app/logout` - Logout
* **GET** `/teachers-app/classes` - Get assigned classes
* **GET** `/teachers-app/students` - Get students by class

#### Section 12: Response Summary Table

| Status | Scenario     | Error Code           | Message                   |
| ------ | ------------ | -------------------- | ------------------------- |
| 200 ✅  | Success      | -                    | Authentication successful |
| 401 ❌  | Invalid      | INVALID\_CREDENTIALS | Phone or PIN incorrect    |
| 429 ❌  | Rate limited | RATE\_LIMITED        | Too many attempts         |

***

### EP005: Teacher Logout

#### Section 1: Endpoint Summary

Invalidates teacher session, effectively logging them out from the app.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/teachers-app/logout`
* **Authentication:** JWT Bearer Token (Staff)

#### Section 5: Request Body Schema

```json theme={null}
{
  "logout_all_devices": "Boolean (optional, default: false)"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Logged out successfully",
  "data": {
    "logged_out_devices": 1,
    "timestamp": "2026-03-16T10:30:45.000Z"
  }
}
```

#### Section 7: Error Responses

**401 - Unauthorized**

```json theme={null}
{
  "status": "error",
  "code": "UNAUTHORIZED",
  "message": "Invalid JWT token"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const logoutTeacher = async (token) => {
  const response = await fetch('https://api.skole.com/v1/teachers-app/logout', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    }
  });

  const data = await response.json();
  if (data.status === 'success') {
    await AsyncStorage.removeItem('teacherToken');
    return true;
  }
  return false;
};
```

#### Section 9: Database Context

**Tables:**

* `staff_devices` - Mark is\_active = 0

#### Section 10: Business Logic & Validations

Mark session\_token as inactive in staff\_devices table.

#### Section 11: Related Endpoints

* **POST** `/teachers-app/teacher-auth-verification` - Login

#### Section 12: Response Summary Table

| Status | Scenario     | Error Code   |
| ------ | ------------ | ------------ |
| 200 ✅  | Success      | -            |
| 401 ❌  | Unauthorized | UNAUTHORIZED |

***

### EP006: Admin Signup

#### Section 1: Endpoint Summary

Creates new admin account with email and password. First admin in a school requires special code/invitation.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/web-app/signup`
* **Authentication:** Public (with optional invitation code)

#### Section 5: Request Body Schema

```json theme={null}
{
  "email*": "String (valid email format)",
  "password*": "String (min 8 chars, uppercase, number, special char)",
  "first_name*": "String",
  "last_name*": "String",
  "school_code": "String (optional, for first admin)",
  "invitation_code": "UUID (optional for additional admins)"
}
```

#### Section 6: Response Schema (Success - 201)

```json theme={null}
{
  "status": "success",
  "message": "Admin account created successfully",
  "data": {
    "id": "UUID",
    "email": "admin@school.com",
    "first_name": "Rajesh",
    "last_name": "Sharma",
    "created_at": "2026-03-16T10:30:45.000Z"
  }
}
```

#### Section 7: Error Responses

**400 - Email Exists**

```json theme={null}
{
  "status": "error",
  "code": "EMAIL_EXISTS",
  "message": "Email already registered"
}
```

**400 - Weak Password**

```json theme={null}
{
  "status": "error",
  "code": "WEAK_PASSWORD",
  "message": "Password must contain uppercase, number, and special character"
}
```

**400 - Invalid Code**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_CODE",
  "message": "School code or invitation code is invalid"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const signupAdmin = async (email, password, firstName, lastName, schoolCode) => {
  const response = await fetch('https://api.skole.com/v1/web-app/signup', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email,
      password,
      first_name: firstName,
      last_name: lastName,
      school_code: schoolCode
    })
  });

  const data = await response.json();
  if (data.status === 'success') {
    console.log('Admin account created');
    return data.data;
  }
  throw new Error(data.message);
};
```

#### Section 9: Database Context

**Tables:**

* `users` - Admin user records
* `schools` - School codes validation

**Password Hashing:** bcrypt (12 rounds)

#### Section 10: Business Logic & Validations

**Validation:**

* Email format valid and unique
* Password strength check (8+ chars, uppercase, number, special char)
* School code exists and valid
* Invitation code not expired

**Business Logic:**

1. Validate email uniqueness
2. Check password strength
3. If school\_code provided, verify and create school + admin
4. If invitation\_code provided, verify it's valid and not expired
5. Hash password with bcrypt
6. Create user and admin\_role records
7. Return confirmation

#### Section 11: Related Endpoints

* **POST** `/web-app/signin` - Login with created account
* **POST** `/web-app/reset-password` - Password reset

#### Section 12: Response Summary Table

| Status | Scenario      | Error Code     | Message               |
| ------ | ------------- | -------------- | --------------------- |
| 201 ✅  | Success       | -              | Account created       |
| 400 ❌  | Email exists  | EMAIL\_EXISTS  | Already registered    |
| 400 ❌  | Weak password | WEAK\_PASSWORD | Password requirements |
| 400 ❌  | Invalid code  | INVALID\_CODE  | Code invalid          |

***

### EP007: Admin Email+Password Login

#### Section 1: Endpoint Summary

Authenticates admin users using email and password. Returns JWT token with admin profile and school information.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/web-app/signin`
* **Authentication:** Public

#### Section 5: Request Body Schema

```json theme={null}
{
  "email*": "String (valid email)",
  "password*": "String",
  "remember_me": "Boolean (optional)"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Login successful",
  "data": {
    "access_token": "JWT token",
    "refresh_token": "UUID (if remember_me=true)",
    "expires_in": 86400,
    "token_type": "Bearer",
    "user": {
      "id": "UUID",
      "email": "admin@school.com",
      "first_name": "Rajesh",
      "last_name": "Sharma",
      "role": "admin"
    },
    "school": {
      "id": "UUID",
      "name": "Greenfield Academy",
      "code": "GFA2024"
    }
  }
}
```

#### Section 7: Error Responses

**401 - Invalid Credentials**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_CREDENTIALS",
  "message": "Email or password is incorrect"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const loginAdmin = async (email, password) => {
  const response = await fetch('https://api.skole.com/v1/web-app/signin', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email,
      password,
      remember_me: true
    })
  });

  const data = await response.json();
  if (data.status === 'success') {
    localStorage.setItem('adminToken', data.data.access_token);
    return data.data;
  }
  throw new Error(data.message);
};
```

#### Section 9: Database Context

**Tables:**

* `users` - User records
* `admin_roles` - Admin role assignments
* `schools` - School details

**Password Verification:** bcrypt.compare()

#### Section 10: Business Logic & Validations

**Validation:**

* Email exists in users table
* Password matches bcrypt hash
* User has admin role
* Account not suspended

**Business Logic:**

1. Find user by email
2. Verify password
3. Check if admin role exists
4. Load school information
5. Generate JWT (1 day expiry)
6. If remember\_me, generate refresh token (30 days)
7. Return tokens and profile

#### Section 11: Related Endpoints

* **POST** `/web-app/reset-password` - Reset password
* **POST** `/web-app/logout` - Logout
* **GET** `/web-app/profile` - Get admin profile

#### Section 12: Response Summary Table

| Status | Scenario  | Error Code           | Message                  |
| ------ | --------- | -------------------- | ------------------------ |
| 200 ✅  | Success   | -                    | Login successful         |
| 401 ❌  | Invalid   | INVALID\_CREDENTIALS | Email/password incorrect |
| 403 ❌  | Not admin | FORBIDDEN            | User not admin           |

***

### EP008: Request Password Reset

#### Section 1: Endpoint Summary

Sends password reset email link for admin accounts. Generates time-limited reset token (valid 1 hour).

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/web-app/reset-password`
* **Authentication:** Public

#### Section 5: Request Body Schema

```json theme={null}
{
  "email*": "String (registered admin email)"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Reset link sent to your email",
  "data": {
    "email": "admin@school.com",
    "reset_token_expires_in": "3600 seconds"
  }
}
```

#### Section 7: Error Responses

**404 - Email Not Found**

```json theme={null}
{
  "status": "error",
  "code": "EMAIL_NOT_FOUND",
  "message": "Email not registered"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const requestPasswordReset = async (email) => {
  const response = await fetch('https://api.skole.com/v1/web-app/reset-password', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email })
  });

  const data = await response.json();
  if (data.status === 'success') {
    console.log('Reset email sent');
    return true;
  }
  throw new Error(data.message);
};
```

#### Section 9: Database Context

**Tables:**

* `password_resets` - Store reset token, email, expires\_at
* TTL: 1 hour, auto-cleanup for expired records

#### Section 10: Business Logic & Validations

**Validation:**

* Email exists in users table
* No active reset token for this email (or expired)

**Business Logic:**

1. Find user by email
2. Generate random reset token (32 chars)
3. Save to password\_resets with 1-hour expiry
4. Send email with reset link
5. Return confirmation (don't expose token)

#### Section 11: Related Endpoints

* **POST** `/web-app/confirm-reset` - Verify token and set new password
* **POST** `/web-app/signin` - Login

#### Section 12: Response Summary Table

| Status | Scenario  | Error Code        | Message              |
| ------ | --------- | ----------------- | -------------------- |
| 200 ✅  | Success   | -                 | Reset link sent      |
| 404 ❌  | Not found | EMAIL\_NOT\_FOUND | Email not registered |

***

### EP009: Set Parent PIN (First Time)

#### Section 1: Endpoint Summary

Parents set their initial 4-digit PIN. Requires parent invitation/verification code from admin. Used during initial parent account activation.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/web-app/parent-set-password`
* **Authentication:** Public (with activation code)

#### Section 5: Request Body Schema

```json theme={null}
{
  "parent_id*": "UUID",
  "activation_code*": "String (from SMS/email invitation)",
  "pin*": "String (4-6 digits only, no special chars)",
  "confirm_pin*": "String (must match pin)"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "PIN set successfully",
  "data": {
    "parent_id": "UUID",
    "pin_set_at": "2026-03-16T10:30:45.000Z",
    "message": "You can now login with your phone and PIN"
  }
}
```

#### Section 7: Error Responses

**400 - Invalid PIN Format**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_PIN_FORMAT",
  "message": "PIN must be 4-6 digits only"
}
```

**400 - PIN Mismatch**

```json theme={null}
{
  "status": "error",
  "code": "PIN_MISMATCH",
  "message": "PIN and confirm PIN do not match"
}
```

**400 - Invalid Activation Code**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_ACTIVATION_CODE",
  "message": "Activation code is invalid or expired"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const setParentPin = async (parentId, activationCode, pin) => {
  const response = await fetch('https://api.skole.com/v1/web-app/parent-set-password', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      parent_id: parentId,
      activation_code: activationCode,
      pin: pin,
      confirm_pin: pin
    })
  });

  const data = await response.json();
  if (data.status === 'success') {
    console.log('PIN set successfully');
    return true;
  }
  throw new Error(data.message);
};
```

#### Section 9: Database Context

**Tables:**

* `parent_details` - Update pin\_hash
* `parent_activations` - Track activation codes (TTL: 7 days)

#### Section 10: Business Logic & Validations

**Validation:**

* PIN format: 4-6 digits only
* PIN matches confirm\_pin
* activation\_code exists and not expired
* parent\_id matches activation code
* Parent doesn't already have a PIN set

**Business Logic:**

1. Validate PIN format
2. Verify activation\_code for this parent\_id
3. Hash PIN with bcrypt
4. Update parent\_details with pin\_hash
5. Mark activation as completed
6. Return success

#### Section 11: Related Endpoints

* **POST** `/parent-app/parent-auth-verification` - Login after PIN set
* **PATCH** `/web-app/parent-edit-password` - Change PIN later

#### Section 12: Response Summary Table

| Status | Scenario       | Error Code                | Message              |
| ------ | -------------- | ------------------------- | -------------------- |
| 200 ✅  | Success        | -                         | PIN set successfully |
| 400 ❌  | Invalid format | INVALID\_PIN\_FORMAT      | Must be 4-6 digits   |
| 400 ❌  | PIN mismatch   | PIN\_MISMATCH             | Pins don't match     |
| 400 ❌  | Code expired   | INVALID\_ACTIVATION\_CODE | Code invalid         |

***

### EP010: Update Parent PIN

#### Section 1: Endpoint Summary

Parents change their existing PIN. Requires old PIN verification for security.

#### Section 2: HTTP Details

* **HTTP Method:** PATCH
* **Endpoint URL:** `/web-app/parent-edit-password`
* **Authentication:** Public (with parent identification)

#### Section 5: Request Body Schema

```json theme={null}
{
  "phone_no*": "String",
  "old_pin*": "String",
  "new_pin*": "String (4-6 digits)",
  "confirm_pin*": "String"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "PIN updated successfully",
  "data": {
    "updated_at": "2026-03-16T10:30:45.000Z"
  }
}
```

#### Section 7: Error Responses

**401 - Old PIN Incorrect**

```json theme={null}
{
  "status": "error",
  "code": "INVALID_OLD_PIN",
  "message": "Old PIN is incorrect"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const updateParentPin = async (phone, oldPin, newPin) => {
  const response = await fetch('https://api.skole.com/v1/web-app/parent-edit-password', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      phone_no: phone,
      old_pin: oldPin,
      new_pin: newPin,
      confirm_pin: newPin
    })
  });

  const data = await response.json();
  return data.status === 'success';
};
```

#### Section 9: Database Context

**Tables:**

* `parent_details` - Update pin\_hash, updated\_at

#### Section 10: Business Logic & Validations

Verify old PIN, validate new PIN format, update in database.

#### Section 11: Related Endpoints

* **POST** `/parent-app/parent-auth-verification` - Login with new PIN
* **POST** `/web-app/parent-set-password` - Initial setup

#### Section 12: Response Summary Table

| Status | Scenario      | Error Code        | Message           |
| ------ | ------------- | ----------------- | ----------------- |
| 200 ✅  | Success       | -                 | PIN updated       |
| 401 ❌  | Wrong old PIN | INVALID\_OLD\_PIN | Incorrect old PIN |

***

### EP011: Set Teacher PIN (First Time)

#### Section 1: Endpoint Summary

Teachers set their initial 4-digit PIN during account activation.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/web-app/staff-set-password`
* **Authentication:** Public (with activation code)

#### Section 5: Request Body Schema

```json theme={null}
{
  "staff_id*": "UUID",
  "activation_code*": "String",
  "pin*": "String (4-6 digits)",
  "confirm_pin*": "String"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "PIN set successfully"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const setTeacherPin = async (staffId, code, pin) => {
  const response = await fetch('https://api.skole.com/v1/web-app/staff-set-password', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      staff_id: staffId,
      activation_code: code,
      pin: pin,
      confirm_pin: pin
    })
  });

  return (await response.json()).status === 'success';
};
```

#### Section 9: Database Context

**Tables:**

* `staff_details` - Update pin\_hash
* `staff_activations` - Verify activation code

#### Section 12: Response Summary Table

| Status | Scenario | Error Code    |
| ------ | -------- | ------------- |
| 200 ✅  | Success  | -             |
| 400 ❌  | Invalid  | FORMAT\_ERROR |

***

### EP012: Update Teacher PIN

#### Section 1: Endpoint Summary

Teachers change their existing PIN with old PIN verification.

#### Section 2: HTTP Details

* **HTTP Method:** PATCH
* **Endpoint URL:** `/web-app/staff-edit-password`
* **Authentication:** Public

#### Section 5: Request Body Schema

```json theme={null}
{
  "phone_no*": "String",
  "old_pin*": "String",
  "new_pin*": "String",
  "confirm_pin*": "String"
}
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "PIN updated successfully"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const updateTeacherPin = async (phone, oldPin, newPin) => {
  const response = await fetch('https://api.skole.com/v1/web-app/staff-edit-password', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      phone_no: phone,
      old_pin: oldPin,
      new_pin: newPin,
      confirm_pin: newPin
    })
  });

  return (await response.json()).status === 'success';
};
```

#### Section 9: Database Context

**Tables:**

* `staff_details` - Update pin\_hash

#### Section 12: Response Summary Table

| Status | Scenario  | Error Code        |
| ------ | --------- | ----------------- |
| 200 ✅  | Success   | -                 |
| 401 ❌  | Wrong PIN | INVALID\_OLD\_PIN |

***

### EP013: Admin Logout

#### Section 1: Endpoint Summary

Invalidates admin JWT token and ends the session.

#### Section 2: HTTP Details

* **HTTP Method:** POST
* **Endpoint URL:** `/web-app/logout`
* **Authentication:** JWT Bearer Token (Required)

#### Section 5: Request Body Schema

```json theme={null}
{} // Empty body
```

#### Section 6: Response Schema (Success - 200)

```json theme={null}
{
  "status": "success",
  "message": "Logged out successfully"
}
```

#### Section 7: Error Responses

**401 - Unauthorized**

```json theme={null}
{
  "status": "error",
  "code": "UNAUTHORIZED",
  "message": "Invalid JWT token"
}
```

#### Section 8: Implementation Examples

**JavaScript:**

```javascript theme={null}
const logoutAdmin = async (token) => {
  const response = await fetch('https://api.skole.com/v1/web-app/logout', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    }
  });

  if (response.ok) {
    localStorage.removeItem('adminToken');
    return true;
  }
  return false;
};
```

#### Section 9: Database Context

**Tables:**

* `sessions` - Revoke session token or mark as inactive

#### Section 10: Business Logic & Validations

Extract and invalidate JWT session.

#### Section 11: Related Endpoints

* **POST** `/web-app/signin` - Login again

#### Section 12: Response Summary Table

| Status | Scenario     | Error Code   |
| ------ | ------------ | ------------ |
| 200 ✅  | Success      | -            |
| 401 ❌  | Unauthorized | UNAUTHORIZED |
