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.
const response = await fetch('/api/school/classes', {
credentials: 'include', // ← REQUIRED for httpOnly cookies
headers: {
'Content-Type': 'application/json'
}
});
// ❌ 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).
GET /api/school/classes
Authorization: Cookie (automatic)
{
"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.
{
"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 titledescription(string): Class descriptionsubject(string): Subject areagradeLevel(string): Grade leveldscId(string): Domain-Specific Competency ID
Optional Fields
language(string, default: "en"): Language codescenarioContext(string): SBCAT scenario contextitsAnalysisLevel(number, 1-10, default: 2): ITS analysis depth
{
"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.
{
"title": "Advanced AI Literacy - Spring 2026",
"description": "Updated course description...",
"itsAnalysisLevel": 4
}
Modifiable Fields
titledescriptionsubjectgradeLevelscenarioContextitsAnalysisLevellanguageenrollmentCode
Immutable Fields
id(class ID)dscId(requires new class)creatorEmailcreatorName
{
"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.
{
"email": "expert@university.edu"
}
{
"success": true,
"class": {
"id": "class-ailc-abc123",
"contentExperts": ["expert@university.edu"],
...
}
}
Error Responses
{
"success": false,
"error": "Only the class owner can invite content experts"
}
{
"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.
{
"email": "expert@university.edu"
}
{
"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.
{
"newOwnerEmail": "newowner@university.edu"
}
{
"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
{
"success": false,
"error": "A pending ownership transfer already exists for this class. Cancel it first."
}
{
"success": false,
"error": "Cannot transfer ownership to yourself"
}
POST /api/school/class/:classId/accept-ownership
Accept a pending ownership transfer. Completes transfer immediately.
POST /api/school/class/{classId}/accept-ownership
(no body required)
{
"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
{
"success": false,
"error": "No pending ownership transfer for this class"
}
{
"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.
POST /api/school/class/{classId}/cancel-transfer
(no body required)
{
"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
{
"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
{
"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
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
- 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
- Cache responses: Store class lists locally and refresh periodically
- Batch operations: Avoid rapid-fire individual updates
- Use query parameters: Filter server-side instead of fetching all data
- Handle errors gracefully: Implement retry logic with exponential backoff
- Monitor performance: Log slow requests and optimize