Files
schulos/API_IMPLEMENTATION_GUIDE.md

7.8 KiB

API Implementation Guide - Module B, F, H

All endpoints follow the standardized response format and use the middleware pattern from src/lib/route-middleware.ts.


Module B: Emergency Signage API

Signage Device Management

POST /api/v1/signage/displays

Request: {
  displayName: string,
  signageType: "digital_display" | "led_board" | "tv_monitor",
  location: string,
  resolution?: string,
  ipAddress?: string,
}

Response: {
  success: true,
  data: { id, displayName, signageType, location, isActive }
}

GET /api/v1/signage/displays

Query: { schoolId: string, isActive?: boolean }
Response: {
  success: true,
  data: EmergencySignage[],
  pagination: { page, total, limit }
}

PATCH /api/v1/signage/displays/{id}

Request: {
  brightness?: number,
  volume?: number,
  isActive?: boolean,
}
Response: { success: true, data: { id, ...updated } }

Message Management

POST /api/v1/signage/messages

Request: {
  signageId: string,
  title: string,
  content: string,
  messageType: "emergency" | "notification" | "schedule" | "announcement",
  priority: "low" | "normal" | "high" | "critical",
  displayDuration: number,
  soundAlert?: boolean,
  backgroundColor?: string,
  textColor?: string,
  startTime?: ISO8601,
  endTime?: ISO8601,
}

Response: {
  success: true,
  data: { id, title, messageType, priority, createdAt }
}

GET /api/v1/signage/messages

Query: { schoolId: string, signageId?: string, type?: string }
Response: {
  success: true,
  data: SignageMessage[],
}

Scheduling

POST /api/v1/signage/schedules

Request: {
  signageId: string,
  messageId?: string,
  dayOfWeek: 0-6,
  startTime: "HH:mm",
  endTime: "HH:mm",
  isRecurring: boolean,
}

Response: { success: true, data: { id, dayOfWeek, startTime, endTime } }

Module F: Exam Mode API

Exam Session Management

POST /api/v1/exams/sessions

Request: {
  assessmentId: string,
  studentId: string,
  durationMinutes: number,
  lockdownEnabled: boolean,
  cameraMonitor?: boolean,
}

Response: {
  success: true,
  data: {
    id,
    studentId,
    status: "not_started",
    startTime,
    environmentNotes,
  }
}

GET /api/v1/exams/sessions/{id}

Response: {
  success: true,
  data: {
    id,
    status,
    timeRemaining,
    score,
    answers: ExamAnswer[],
    events: ExamEvent[],
    warnings: ExamWarning[],
  }
}

GET /api/v1/exams/active-sessions

Query: { schoolId: string, classGroupId?: string }
Response: {
  success: true,
  data: ExamSession[] (with student, assessment, events)
}

Answer Submission

POST /api/v1/exams/sessions/{id}/answers

Request: {
  questionId: string,
  answer: string, // JSON serialized
}

Response: {
  success: true,
  data: { answerId, questionId, submittedAt }
}

PATCH /api/v1/exams/sessions/{id}/submit

Request: { force?: boolean }
Response: {
  success: true,
  data: {
    id,
    status: "submitted",
    submittedAt,
    score,
    totalPoints,
  }
}

Event Logging

POST /api/v1/exams/sessions/{id}/events

Request: {
  eventType: "tab_switch" | "copy_paste" | "window_blur" | "camera_detected_none" | "suspicious_movement",
  severity: "info" | "warning" | "critical",
  description?: string,
  metadata?: Record<string, any>,
}

Response: {
  success: true,
  data: { eventId, eventType, severity, timestamp }
}

Monitoring & Security

GET /api/v1/exams/{id}/security-report

Response: {
  success: true,
  data: {
    suspiciousEvents: number,
    tabSwitches: number,
    copyCounts: number,
    focusLosses: number,
    riskScore: 0-100,
    events: ExamEvent[],
    warnings: ExamWarning[],
  }
}

POST /api/v1/exams/sessions/{id}/warnings

Request: {
  warningType: "time_low" | "behavior_suspicious" | "multiple_tabs" | "camera_issue",
  message: string,
}

Response: { success: true, data: { id, warningType, createdAt } }

Module H: Notifications API

Notification Hub

GET /api/v1/notifications/hub

Query: { 
  userId: string,
  limit?: number,
  category?: string,
}

Response: {
  success: true,
  data: NotificationHub[],
  pagination: { unreadCount, totalCount }
}

POST /api/v1/notifications

Request: {
  userId: string,
  category: "assessment" | "behavior" | "communication" | "wellness" | "system",
  priority: "low" | "normal" | "high" | "critical",
  title: string,
  message: string,
  actionUrl?: string,
  actionLabel?: string,
  sourceType?: string,
  sourceId?: string,
  expiresAt?: ISO8601,
}

Response: {
  success: true,
  data: { id, userId, category, priority, createdAt }
}

Notification Actions

PATCH /api/v1/notifications/{id}/read

Response: { success: true, data: { id, isRead: true, readAt } }

PATCH /api/v1/notifications/{id}/dismiss

Response: { success: true, data: { id, isDismissed: true, dismissedAt } }

PATCH /api/v1/notifications/{id}/archive

Response: { success: true, data: { id, archivedAt } }

Bulk Operations

POST /api/v1/notifications/batch

Request: {
  userIds: string[],
  category: string,
  priority: string,
  title: string,
  message: string,
}

Response: {
  success: true,
  data: { createdCount: number }
}

PATCH /api/v1/notifications/batch/read

Request: { notificationIds: string[] }
Response: { success: true, data: { updatedCount: number } }

Preferences

GET /api/v1/notifications/preferences

Query: { userId: string }
Response: {
  success: true,
  data: NotificationPreferences[]
}

PATCH /api/v1/notifications/preferences

Request: {
  category: string,
  channel: "email" | "in_app" | "push" | "sms",
  enabled: boolean,
}

Response: { success: true, data: { id, category, channel, enabled } }

Implementation Template

All routes should follow this pattern:

import { withAuth } from '@/lib/route-middleware';
import { successResponse, errorResponse } from '@/lib/api-response';
import { logComplianceAudit } from '@/lib/audit';

export const POST = withAuth(
  ['SCHOOL_ADMIN', 'TEACHER'],
  async (req: Request, session: Session) => {
    try {
      // Validate input
      const body = await req.json();
      
      // Check authorization
      if (body.schoolId !== session.user.schoolId) {
        return errorResponse('Unauthorized', 403);
      }

      // Create resource
      const resource = await db.model.create({
        data: body,
      });

      // Log audit
      await logComplianceAudit({
        userId: session.user.id,
        schoolId: session.user.schoolId,
        action: 'CREATE',
        entityType: 'Model',
        entityId: resource.id,
      });

      return successResponse(resource);
    } catch (error) {
      return errorResponse('Internal server error', 500);
    }
  }
);

Testing Checklist

For each endpoint:

  • Happy path (valid input)
  • Missing required fields
  • Invalid field types
  • Unauthorized access (wrong school)
  • Rate limiting
  • Audit logging
  • Response format compliance

Performance Notes

Indices needed:

CREATE INDEX idx_exam_sessions_status ON exam_sessions(status);
CREATE INDEX idx_exam_events_session ON exam_events(examSessionId);
CREATE INDEX idx_notifications_user_read ON notification_hubs(userId, isRead);
CREATE INDEX idx_signage_school_active ON emergency_signages(schoolId, isActive);

Caching strategy:

  • Signage devices: 5 minutes
  • Active exams: 30 seconds
  • Notifications: 1 minute (invalidate on create)
  • User preferences: 1 hour

Pagination defaults:

  • Default limit: 20
  • Max limit: 100
  • Default page: 1