Class Management API Reference

Complete technical reference for all class management API endpoints. This guide covers request formats, response structures, authentication requirements, and error handling.

Authentication

All class management endpoints require authentication using UALS Auth System v2.0 with httpOnly cookies. Never use manual Authorization headers or localStorage tokens.

✅ CORRECT Authentication Pattern
const response = await fetch('/api/school/classes', {
  credentials: 'include',  // ← REQUIRED for httpOnly cookies
  headers: {
    'Content-Type': 'application/json'
  }
});
❌ WRONG - Never Use These Patterns
// ❌ Manual Authorization header
fetch('/api/school/classes', {
  headers: { 'Authorization': 'Bearer ' + token }
});

// ❌ Missing credentials: 'include'
fetch('/api/school/classes', {
  headers: { 'Content-Type': 'application/json' }
});

Role Requirements

Most class management endpoints require educator or admin role.

Endpoint Required Role
GET /api/school/classes educator, admin
POST /api/school/classes educator, admin
PATCH /api/school/class/:classId educator (owner), admin
POST /api/school/class/:classId/invite-expert educator (owner), admin
POST /api/school/class/:classId/remove-expert educator (owner), admin
POST /api/school/class/:classId/transfer-ownership educator (owner), admin
POST /api/school/class/:classId/accept-ownership educator (invitee), admin
POST /api/school/class/:classId/cancel-transfer educator (owner), admin

API Endpoints

GET /api/school/classes

Retrieve all classes accessible to the authenticated user (owned or invited as expert).

Request
GET /api/school/classes
Authorization: Cookie (automatic)
Response - 200 OK
{
  "success": true,
  "classes": [
    {
      "id": "class-ailc-abc123",
      "title": "AI Literacy - Fall 2025",
      "description": "Introduction to AI concepts...",
      "subject": "Computer Science",
      "gradeLevel": "College",
      "dscId": "dsc-comp-ailc",
      "enrollmentCode": "ENROLL-XYZ789",
      "teacherEmail": "owner@school.edu",
      "teacherName": "Dr. Jane Smith",
      "creatorEmail": "creator@school.edu",
      "creatorName": "Dr. Original Creator",
      "contentExperts": ["expert1@university.edu", "expert2@college.edu"],
      "ownershipTransferPending": null,
      "createdAt": "2025-11-01T10:00:00.000Z"
    }
  ]
}

POST /api/school/classes

Create a new class. Authenticated user becomes both creator and owner.

Request Body
{
  "title": "AP Computer Science A - Fall 2025",
  "description": "Advanced placement course...",
  "subject": "Computer Science",
  "gradeLevel": "High School",
  "dscId": "dsc-comp-prog",
  "language": "en",
  "scenarioContext": "You are a software engineer...",
  "itsAnalysisLevel": 3
}

Required Fields

  • title (string): Class title
  • description (string): Class description
  • subject (string): Subject area
  • gradeLevel (string): Grade level
  • dscId (string): Domain-Specific Competency ID

Optional Fields

  • language (string, default: "en"): Language code
  • scenarioContext (string): SBCAT scenario context
  • itsAnalysisLevel (number, 1-10, default: 2): ITS analysis depth
Response - 201 Created
{
  "success": true,
  "class": {
    "id": "class-prog-def456",
    "title": "AP Computer Science A - Fall 2025",
    "enrollmentCode": "ENROLL-ABC123",
    "teacherEmail": "teacher@school.edu",
    "creatorEmail": "teacher@school.edu",
    "contentExperts": [],
    ...
  }
}

PATCH /api/school/class/:classId

Update class details. Only the owner can modify class settings.

Request Body
{
  "title": "Advanced AI Literacy - Spring 2026",
  "description": "Updated course description...",
  "itsAnalysisLevel": 4
}

Modifiable Fields

  • title
  • description
  • subject
  • gradeLevel
  • scenarioContext
  • itsAnalysisLevel
  • language
  • enrollmentCode

Immutable Fields

  • id (class ID)
  • dscId (requires new class)
  • creatorEmail
  • creatorName
Response - 200 OK
{
  "success": true,
  "class": { /* updated class object */ }
}

POST /api/school/class/:classId/invite-expert

Invite a teacher as a content expert. Expert gains immediate editing access.

Request Body
{
  "email": "expert@university.edu"
}
Response - 200 OK
{
  "success": true,
  "class": {
    "id": "class-ailc-abc123",
    "contentExperts": ["expert@university.edu"],
    ...
  }
}

Error Responses

403 Forbidden - Not Owner
{
  "success": false,
  "error": "Only the class owner can invite content experts"
}
400 Bad Request - Already Invited
{
  "success": false,
  "error": "This email is already a content expert for this class"
}

POST /api/school/class/:classId/remove-expert

Remove a content expert from the class. Access is revoked immediately.

Request Body
{
  "email": "expert@university.edu"
}
Response - 200 OK
{
  "success": true,
  "class": {
    "id": "class-ailc-abc123",
    "contentExperts": [],  // expert removed
    ...
  }
}

POST /api/school/class/:classId/transfer-ownership

Initiate ownership transfer to another teacher. Transfer enters pending state.

Request Body
{
  "newOwnerEmail": "newowner@university.edu"
}
Response - 200 OK
{
  "success": true,
  "class": {
    "id": "class-ailc-abc123",
    "teacherEmail": "currentowner@school.edu",
    "ownershipTransferPending": "{\"newOwnerEmail\":\"newowner@university.edu\",\"initiatedAt\":\"2025-11-23T10:30:00.000Z\",\"initiatedBy\":\"currentowner@school.edu\"}",
    ...
  }
}

Error Responses

400 Bad Request - Transfer Already Pending
{
  "success": false,
  "error": "A pending ownership transfer already exists for this class. Cancel it first."
}
400 Bad Request - Transfer to Self
{
  "success": false,
  "error": "Cannot transfer ownership to yourself"
}

POST /api/school/class/:classId/accept-ownership

Accept a pending ownership transfer. Completes transfer immediately.

Request
POST /api/school/class/{classId}/accept-ownership
(no body required)
Response - 200 OK
{
  "success": true,
  "class": {
    "id": "class-ailc-abc123",
    "teacherEmail": "newowner@university.edu",
    "teacherName": "Prof. New Owner",
    "creatorEmail": "originalcreator@school.edu",
    "ownershipTransferPending": null,
    "contentExperts": [],  // cleared on transfer
    ...
  }
}

Error Responses

400 Bad Request - No Pending Transfer
{
  "success": false,
  "error": "No pending ownership transfer for this class"
}
403 Forbidden - Wrong Recipient
{
  "success": false,
  "error": "This ownership transfer is not for you"
}

POST /api/school/class/:classId/cancel-transfer

Cancel a pending ownership transfer. Only current owner can cancel.

Request
POST /api/school/class/{classId}/cancel-transfer
(no body required)
Response - 200 OK
{
  "success": true,
  "class": {
    "id": "class-ailc-abc123",
    "ownershipTransferPending": null,
    ...
  }
}

xAPI Data Structure

All class data is stored in xAPI LRS statements. Understanding the structure is important for debugging and custom integrations.

Class Creation Statement

xAPI Statement - Class Created
{
  "actor": {
    "mbox": "mailto:teacher@school.edu",
    "name": "Dr. Jane Smith",
    "objectType": "Agent"
  },
  "verb": {
    "id": "http://adlnet.gov/expapi/verbs/initialized",
    "display": { "en": "initialized" }
  },
  "object": {
    "id": "https://uals.app/class/class-ailc-abc123",
    "objectType": "Activity",
    "definition": {
      "name": { "en": "AI Literacy - Fall 2025" },
      "description": { "en": "Introduction to AI concepts..." }
    }
  },
  "context": {
    "extensions": {
      "http://uals.app/class/id": "class-ailc-abc123",
      "http://uals.app/class/title": "AI Literacy - Fall 2025",
      "http://uals.app/class/description": "Introduction to AI concepts...",
      "http://uals.app/class/subject": "Computer Science",
      "http://uals.app/class/grade-level": "College",
      "http://uals.app/class/dsc-id": "dsc-comp-ailc",
      "http://uals.app/class/enrollment-code": "ENROLL-XYZ789",
      "http://uals.app/class/teacher-email": "teacher@school.edu",
      "http://uals.app/class/teacher-name": "Dr. Jane Smith",
      "http://uals.app/class/creator-email": "teacher@school.edu",
      "http://uals.app/class/creator-name": "Dr. Jane Smith",
      "http://uals.app/class/content-experts": "[]",
      "http://uals.app/class/ownership-transfer-pending": null,
      "http://uals.app/class/language": "en",
      "http://uals.app/class/scenario-context": "You are a policy advisor...",
      "http://uals.app/class/its-analysis-level": 3
    }
  },
  "timestamp": "2025-11-23T10:00:00.000Z"
}

Extension Fields Reference

Extension Key Type Description Mutable
http://uals.app/class/id string Unique class identifier
http://uals.app/class/title string Class title
http://uals.app/class/description string Class description
http://uals.app/class/teacher-email string Current owner email ✅ (via transfer)
http://uals.app/class/teacher-name string Current owner name ✅ (via transfer)
http://uals.app/class/creator-email string Original creator email
http://uals.app/class/creator-name string Original creator name
http://uals.app/class/content-experts JSON string Array of expert emails
http://uals.app/class/ownership-transfer-pending JSON string or null Pending transfer details
http://uals.app/class/enrollment-code string Student enrollment code
http://uals.app/class/dsc-id string DSC identifier

Error Handling

Standard Error Response Format

Error Response Structure
{
  "success": false,
  "error": "Human-readable error message",
  "details": {  // optional
    "field": "fieldName",
    "reason": "detailed explanation"
  }
}

HTTP Status Codes

Status Code Meaning Common Scenarios
200 OK Request successful GET, PATCH, POST (update operations)
201 Created Resource created POST /api/school/classes
400 Bad Request Invalid request data Missing fields, invalid format, duplicate
401 Unauthorized Not authenticated Missing or invalid auth cookie
403 Forbidden Insufficient permissions Not owner, wrong role
404 Not Found Resource doesn't exist Invalid class ID
500 Internal Server Error Server-side error LRS connection failure, unexpected bug

Client-Side Error Handling

Recommended Pattern
async function inviteContentExpert(classId, email) {
  try {
    const response = await fetch(`/api/school/class/${classId}/invite-expert`, {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email })
    });

    const result = await response.json();

    if (!response.ok) {
      // Handle specific errors
      if (response.status === 403) {
        alert('You do not have permission to invite experts.');
      } else if (response.status === 400) {
        alert(`Error: ${result.error}`);
      } else {
        alert('An unexpected error occurred. Please try again.');
      }
      throw new Error(result.error);
    }

    // Success
    return result.class;

  } catch (error) {
    console.error('Failed to invite expert:', error);
    throw error;
  }
}

Rate Limiting & Best Practices

⚠️ Rate Limits
  • 10 requests per minute per user for class management operations
  • 100 requests per hour per user for read-only operations
  • Exceeding limits results in 429 Too Many Requests response

Best Practices

  1. Cache responses: Store class lists locally and refresh periodically
  2. Batch operations: Avoid rapid-fire individual updates
  3. Use query parameters: Filter server-side instead of fetching all data
  4. Handle errors gracefully: Implement retry logic with exponential backoff
  5. Monitor performance: Log slow requests and optimize