University Block

The University Block provides a complete Learning Management System (LMS) API for building educational platforms, corporate training systems, and e-learning applications. Create courses, track progress, assess learners, and issue certificates - all through a simple REST API.
Features
- Course Management - Create structured courses with modules, lessons, and rich content
- Progress Tracking - Monitor learner advancement in real-time
- Quizzes & Assessments - Multiple question types with automatic grading
- Certificates - Issue verifiable certificates on completion
- Learning Paths - Define prerequisites and guided curricula
- Enrollment Management - Control access and manage cohorts
- Analytics & Reporting - Measure learning outcomes and engagement
- Multi-tenant Support - Isolated data per organization
API Endpoint
| Service | URL |
|---|---|
| University | https://university.api.us.23blocks.com |
Environment Routing: Use the same URL for all environments. Your API key determines the environment:
pk_test_*/sk_test_*→ Routes to Stagingpk_live_*/sk_live_*→ Routes to Production
Quick Start
npm install @23blocks/sdk
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { university: 'https://university.api.us.23blocks.com' },
apiKey: 'your-api-key', // Use pk_test_* for staging, pk_live_* for production
});
// Create a course
const course = await client.university.createCourse({
title: 'Introduction to JavaScript',
description: 'Learn the fundamentals of JavaScript programming',
status: 'published'
});
// Enroll a user
await client.university.enroll({
userId: 'user-123',
courseId: course.id
});
// Track lesson completion
await client.university.completeLesson({
userId: 'user-123',
lessonId: 'lesson-abc',
timeSpent: 1800
});
Authentication
All API requests require authentication via API key headers:
curl -X GET "https://api.23blocks.com/v1/university/courses" \
-H "Content-Type: application/json" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key"
Courses API
List Courses
Retrieve all courses with optional filtering.
GET /v1/university/courses
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: draft, published, archived |
category | string | Filter by category ID |
instructor_id | string | Filter by instructor |
page | integer | Page number (default: 1) |
per_page | integer | Items per page (default: 20, max: 100) |
Response:
{
"data": [
{
"id": "course-abc123",
"type": "course",
"attributes": {
"title": "Introduction to JavaScript",
"description": "Learn the fundamentals of JavaScript programming",
"slug": "intro-to-javascript",
"status": "published",
"thumbnail_url": "https://cdn.23blocks.com/courses/js-thumb.png",
"duration_minutes": 480,
"module_count": 8,
"lesson_count": 24,
"enrollment_count": 1250,
"rating": 4.8,
"settings": {
"self_enrollment": true,
"certificate_enabled": true,
"discussion_enabled": true,
"drip_content": false
},
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-03-20T14:30:00Z"
},
"relationships": {
"instructor": {
"data": { "type": "user", "id": "user-456" }
},
"category": {
"data": { "type": "category", "id": "cat-dev" }
}
}
}
],
"meta": {
"total": 45,
"page": 1,
"per_page": 20
}
}
Create Course
Create a new course.
POST /v1/university/courses
Request Body:
{
"data": {
"type": "course",
"attributes": {
"title": "Advanced React Patterns",
"description": "Master advanced React patterns and best practices",
"category_id": "cat-frontend",
"instructor_id": "user-instructor",
"status": "draft",
"settings": {
"self_enrollment": true,
"certificate_enabled": true,
"passing_score": 80,
"max_attempts": 3
}
}
}
}
Get Course
Retrieve a specific course with its structure.
GET /v1/university/courses/{course_id}
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include related resources: modules, lessons, instructor |
Update Course
Update course details.
PATCH /v1/university/courses/{course_id}
Delete Course
Delete a course (soft delete).
DELETE /v1/university/courses/{course_id}
Modules API
Modules organize lessons within a course.
List Modules
GET /v1/university/courses/{course_id}/modules
Response:
{
"data": [
{
"id": "module-001",
"type": "module",
"attributes": {
"title": "Getting Started",
"description": "Introduction and environment setup",
"position": 1,
"lesson_count": 4,
"duration_minutes": 45,
"unlock_at": null
}
},
{
"id": "module-002",
"type": "module",
"attributes": {
"title": "Core Concepts",
"description": "Fundamental patterns and techniques",
"position": 2,
"lesson_count": 6,
"duration_minutes": 90,
"unlock_at": "2025-02-01T00:00:00Z"
}
}
]
}
Create Module
POST /v1/university/courses/{course_id}/modules
Request Body:
{
"data": {
"type": "module",
"attributes": {
"title": "Advanced Hooks",
"description": "Deep dive into React hooks",
"position": 3,
"unlock_at": null
}
}
}
Reorder Modules
POST /v1/university/courses/{course_id}/modules/reorder
Request Body:
{
"module_ids": ["module-002", "module-001", "module-003"]
}
Lessons API
List Lessons
GET /v1/university/modules/{module_id}/lessons
Response:
{
"data": [
{
"id": "lesson-001",
"type": "lesson",
"attributes": {
"title": "Introduction to Hooks",
"description": "Understanding the motivation behind hooks",
"position": 1,
"content_type": "video",
"content": {
"video_url": "https://cdn.23blocks.com/videos/hooks-intro.mp4",
"duration_seconds": 720,
"transcript_url": "https://cdn.23blocks.com/transcripts/hooks-intro.vtt"
},
"free_preview": true,
"created_at": "2025-01-15T10:00:00Z"
}
},
{
"id": "lesson-002",
"type": "lesson",
"attributes": {
"title": "useState Deep Dive",
"description": "Mastering the useState hook",
"position": 2,
"content_type": "text",
"content": {
"html": "<h2>Understanding useState</h2><p>The useState hook...</p>",
"estimated_reading_minutes": 12
},
"free_preview": false
}
}
]
}
Create Lesson
POST /v1/university/modules/{module_id}/lessons
Content Types:
| Type | Description |
|---|---|
video | Video content with optional transcript |
text | Rich text/HTML content |
document | PDF or downloadable file |
embed | External embed (YouTube, Vimeo, etc.) |
quiz | Inline quiz assessment |
assignment | Submission-based assignment |
scorm | SCORM package |
Request Body (Video):
{
"data": {
"type": "lesson",
"attributes": {
"title": "Building Custom Hooks",
"description": "Learn to create reusable custom hooks",
"content_type": "video",
"content": {
"video_url": "https://cdn.23blocks.com/videos/custom-hooks.mp4",
"duration_seconds": 1800,
"allow_download": false
},
"position": 3
}
}
}
Request Body (Text):
{
"data": {
"type": "lesson",
"attributes": {
"title": "Hook Rules and Best Practices",
"content_type": "text",
"content": {
"html": "<h2>Rules of Hooks</h2><p>There are two important rules...</p>",
"estimated_reading_minutes": 8
}
}
}
}
Enrollments API
List Enrollments
Get all enrollments for a course or user.
GET /v1/university/enrollments
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
course_id | string | Filter by course |
user_id | string | Filter by user |
status | string | Filter by status: active, completed, expired, cancelled |
Response:
{
"data": [
{
"id": "enrollment-xyz",
"type": "enrollment",
"attributes": {
"status": "active",
"progress_percent": 65,
"lessons_completed": 16,
"total_lessons": 24,
"time_spent_minutes": 320,
"last_activity_at": "2025-03-20T15:30:00Z",
"enrolled_at": "2025-02-01T10:00:00Z",
"expires_at": null,
"completed_at": null
},
"relationships": {
"user": { "data": { "type": "user", "id": "user-123" } },
"course": { "data": { "type": "course", "id": "course-abc" } }
}
}
]
}
Enroll User
POST /v1/university/enrollments
Request Body:
{
"data": {
"type": "enrollment",
"attributes": {
"user_id": "user-123",
"course_id": "course-abc",
"expires_at": "2025-12-31T23:59:59Z"
}
}
}
Bulk Enroll
Enroll multiple users at once.
POST /v1/university/enrollments/bulk
Request Body:
{
"course_id": "course-abc",
"user_ids": ["user-001", "user-002", "user-003"],
"expires_at": "2025-12-31T23:59:59Z",
"send_notification": true
}
Cancel Enrollment
DELETE /v1/university/enrollments/{enrollment_id}
Progress API
Get User Progress
Get detailed progress for a user in a course.
GET /v1/university/progress/{course_id}/users/{user_id}
Response:
{
"data": {
"id": "progress-xyz",
"type": "progress",
"attributes": {
"course_id": "course-abc",
"user_id": "user-123",
"overall_progress": 65.5,
"status": "in_progress",
"modules": [
{
"module_id": "module-001",
"title": "Getting Started",
"progress": 100,
"completed_at": "2025-02-05T12:00:00Z"
},
{
"module_id": "module-002",
"title": "Core Concepts",
"progress": 66.7,
"completed_at": null
}
],
"lessons_completed": [
{
"lesson_id": "lesson-001",
"completed_at": "2025-02-01T14:30:00Z",
"time_spent_seconds": 780
}
],
"current_lesson": {
"lesson_id": "lesson-010",
"position": 45,
"total": 100
},
"total_time_spent_minutes": 320,
"started_at": "2025-02-01T10:00:00Z",
"last_activity_at": "2025-03-20T15:30:00Z"
}
}
}
Complete Lesson
Mark a lesson as completed.
POST /v1/university/lessons/{lesson_id}/complete
Request Body:
{
"user_id": "user-123",
"time_spent_seconds": 720,
"position": 100
}
Update Progress Position
Save video position or scroll progress.
PATCH /v1/university/lessons/{lesson_id}/progress
Request Body:
{
"user_id": "user-123",
"position": 45,
"time_spent_seconds": 300
}
Reset Progress
Reset user progress for a course.
POST /v1/university/progress/{course_id}/users/{user_id}/reset
Quizzes API
Create Quiz
POST /v1/university/quizzes
Request Body:
{
"data": {
"type": "quiz",
"attributes": {
"title": "Module 1 Assessment",
"description": "Test your understanding of the basics",
"lesson_id": "lesson-005",
"time_limit_minutes": 30,
"passing_score": 80,
"max_attempts": 3,
"shuffle_questions": true,
"show_correct_answers": "after_submission",
"questions": [
{
"type": "multiple_choice",
"question": "What hook is used for side effects?",
"options": [
{ "text": "useState", "correct": false },
{ "text": "useEffect", "correct": true },
{ "text": "useContext", "correct": false },
{ "text": "useReducer", "correct": false }
],
"points": 10,
"explanation": "useEffect is the hook designed for handling side effects."
},
{
"type": "true_false",
"question": "Hooks can be called inside loops.",
"correct_answer": false,
"points": 5,
"explanation": "Hooks must be called at the top level, not inside loops or conditions."
},
{
"type": "fill_blank",
"question": "The ____ hook is used to access context values.",
"correct_answers": ["useContext"],
"case_sensitive": false,
"points": 10
},
{
"type": "matching",
"question": "Match the hook to its purpose:",
"pairs": [
{ "left": "useState", "right": "Local component state" },
{ "left": "useEffect", "right": "Side effects" },
{ "left": "useRef", "right": "Mutable references" }
],
"points": 15
},
{
"type": "essay",
"question": "Explain when you would use useReducer over useState.",
"min_words": 50,
"max_words": 500,
"points": 20,
"rubric": "Look for: complex state logic, multiple sub-values, next state depends on previous"
}
]
}
}
}
Get Quiz
GET /v1/university/quizzes/{quiz_id}
Submit Quiz Attempt
POST /v1/university/quizzes/{quiz_id}/attempts
Request Body:
{
"user_id": "user-123",
"answers": [
{ "question_index": 0, "answer": 1 },
{ "question_index": 1, "answer": false },
{ "question_index": 2, "answer": "useContext" },
{ "question_index": 3, "answer": [[0,0], [1,1], [2,2]] },
{ "question_index": 4, "answer": "I would use useReducer when..." }
],
"time_taken_seconds": 1200
}
Response:
{
"data": {
"id": "attempt-abc",
"type": "quiz_attempt",
"attributes": {
"quiz_id": "quiz-xyz",
"user_id": "user-123",
"score": 85,
"passed": true,
"time_taken_seconds": 1200,
"attempt_number": 1,
"results": [
{ "question_index": 0, "correct": true, "points_earned": 10 },
{ "question_index": 1, "correct": true, "points_earned": 5 },
{ "question_index": 2, "correct": true, "points_earned": 10 },
{ "question_index": 3, "correct": true, "points_earned": 15 },
{ "question_index": 4, "pending_review": true, "points_earned": 0 }
],
"submitted_at": "2025-03-20T16:00:00Z"
}
}
}
Get Quiz Attempts
GET /v1/university/quizzes/{quiz_id}/attempts?user_id={user_id}
Grade Essay Question
PATCH /v1/university/attempts/{attempt_id}/grade
Request Body:
{
"question_index": 4,
"points_earned": 18,
"feedback": "Excellent explanation with clear examples."
}
Certificates API
Certificate Templates
List and manage certificate templates.
GET /v1/university/certificate-templates
Response:
{
"data": [
{
"id": "template-001",
"type": "certificate_template",
"attributes": {
"name": "Course Completion",
"description": "Standard completion certificate",
"html_template": "<div class='cert'>...</div>",
"css_styles": ".cert { ... }",
"variables": ["student_name", "course_name", "completion_date", "instructor_name"],
"paper_size": "A4",
"orientation": "landscape"
}
}
]
}
Create Template
POST /v1/university/certificate-templates
Issue Certificate
POST /v1/university/certificates
Request Body:
{
"data": {
"type": "certificate",
"attributes": {
"user_id": "user-123",
"course_id": "course-abc",
"template_id": "template-001",
"custom_fields": {
"grade": "A",
"distinction": true
}
}
}
}
Response:
{
"data": {
"id": "cert-xyz789",
"type": "certificate",
"attributes": {
"certificate_number": "CERT-2025-0001234",
"user_id": "user-123",
"course_id": "course-abc",
"issued_at": "2025-03-20T16:30:00Z",
"verification_url": "https://verify.23blocks.com/cert/CERT-2025-0001234",
"pdf_url": "https://cdn.23blocks.com/certificates/cert-xyz789.pdf",
"valid": true
}
}
}
Verify Certificate
Public endpoint for certificate verification.
GET /v1/university/certificates/verify/{certificate_number}
Response:
{
"valid": true,
"certificate": {
"certificate_number": "CERT-2025-0001234",
"recipient_name": "John Doe",
"course_name": "Introduction to JavaScript",
"issued_by": "TechCorp Academy",
"issued_at": "2025-03-20T16:30:00Z",
"status": "valid"
}
}
Revoke Certificate
POST /v1/university/certificates/{certificate_id}/revoke
Request Body:
{
"reason": "Academic dishonesty detected"
}
Learning Paths API
List Learning Paths
GET /v1/university/learning-paths
Response:
{
"data": [
{
"id": "path-frontend",
"type": "learning_path",
"attributes": {
"title": "Frontend Developer Path",
"description": "Become a professional frontend developer",
"total_courses": 5,
"total_duration_hours": 40,
"courses": [
{
"course_id": "course-html",
"position": 1,
"required": true,
"prerequisites": []
},
{
"course_id": "course-css",
"position": 2,
"required": true,
"prerequisites": ["course-html"]
},
{
"course_id": "course-js",
"position": 3,
"required": true,
"prerequisites": ["course-html", "course-css"]
},
{
"course_id": "course-react",
"position": 4,
"required": true,
"prerequisites": ["course-js"]
},
{
"course_id": "course-testing",
"position": 5,
"required": false,
"prerequisites": ["course-react"]
}
],
"certificate_id": "template-path-completion"
}
}
]
}
Create Learning Path
POST /v1/university/learning-paths
Get User Path Progress
GET /v1/university/learning-paths/{path_id}/users/{user_id}/progress
Response:
{
"data": {
"path_id": "path-frontend",
"user_id": "user-123",
"overall_progress": 60,
"courses_completed": 3,
"total_courses": 5,
"current_course": "course-react",
"next_available": ["course-react"],
"locked": ["course-testing"],
"time_invested_hours": 24,
"estimated_completion_hours": 16
}
}
Analytics API
Course Analytics
GET /v1/university/analytics/courses/{course_id}
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
period | string | Time period: 7d, 30d, 90d, 1y, all |
metrics | string | Comma-separated: enrollments, completions, engagement, ratings |
Response:
{
"data": {
"course_id": "course-abc",
"period": "30d",
"metrics": {
"enrollments": {
"total": 245,
"new": 82,
"trend": 15.3
},
"completions": {
"total": 156,
"rate": 63.7,
"average_days_to_complete": 14
},
"engagement": {
"active_learners": 189,
"average_time_per_session_minutes": 28,
"lessons_completed_per_day": 4.2,
"drop_off_lessons": [
{ "lesson_id": "lesson-015", "title": "Advanced Patterns", "drop_rate": 23.5 }
]
},
"ratings": {
"average": 4.7,
"count": 134,
"distribution": { "5": 89, "4": 32, "3": 10, "2": 2, "1": 1 }
}
},
"timeline": [
{ "date": "2025-03-01", "enrollments": 12, "completions": 8 },
{ "date": "2025-03-02", "enrollments": 15, "completions": 5 }
]
}
}
User Analytics
GET /v1/university/analytics/users/{user_id}
Response:
{
"data": {
"user_id": "user-123",
"summary": {
"courses_enrolled": 8,
"courses_completed": 5,
"total_time_hours": 67,
"certificates_earned": 4,
"average_score": 88.5,
"current_streak_days": 12
},
"learning_activity": [
{ "date": "2025-03-19", "time_minutes": 45, "lessons_completed": 3 },
{ "date": "2025-03-20", "time_minutes": 60, "lessons_completed": 4 }
],
"strengths": ["JavaScript", "React", "CSS"],
"recommendations": [
{ "course_id": "course-typescript", "reason": "Based on JavaScript completion" }
]
}
}
Leaderboard
GET /v1/university/analytics/leaderboard
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
scope | string | Scope: course, path, global |
course_id | string | Required if scope is course |
metric | string | Ranking metric: progress, score, time, streak |
limit | integer | Number of entries (default: 10) |
Discussion Forums API
List Discussions
GET /v1/university/courses/{course_id}/discussions
Response:
{
"data": [
{
"id": "discussion-001",
"type": "discussion",
"attributes": {
"title": "Question about useEffect cleanup",
"content": "I'm confused about when cleanup functions run...",
"author_id": "user-456",
"lesson_id": "lesson-008",
"reply_count": 5,
"upvotes": 12,
"pinned": false,
"resolved": true,
"created_at": "2025-03-18T10:00:00Z"
}
}
]
}
Create Discussion
POST /v1/university/courses/{course_id}/discussions
Reply to Discussion
POST /v1/university/discussions/{discussion_id}/replies
Mark as Resolved
PATCH /v1/university/discussions/{discussion_id}/resolve
Assignments API
Create Assignment
POST /v1/university/assignments
Request Body:
{
"data": {
"type": "assignment",
"attributes": {
"title": "Build a Todo App",
"description": "Create a fully functional todo application using React hooks",
"lesson_id": "lesson-020",
"due_date": "2025-04-01T23:59:59Z",
"max_score": 100,
"allow_late_submission": true,
"late_penalty_percent": 10,
"rubric": [
{ "criteria": "Functionality", "max_points": 40, "description": "All features working correctly" },
{ "criteria": "Code Quality", "max_points": 30, "description": "Clean, readable code" },
{ "criteria": "UI/UX", "max_points": 20, "description": "User-friendly interface" },
{ "criteria": "Documentation", "max_points": 10, "description": "README and comments" }
],
"allowed_file_types": [".zip", ".tar.gz"],
"max_file_size_mb": 50
}
}
}
Submit Assignment
POST /v1/university/assignments/{assignment_id}/submissions
Request Body (multipart/form-data):
user_id: user-123
file: [binary]
notes: "Implemented all features including bonus dark mode."
Grade Submission
PATCH /v1/university/submissions/{submission_id}/grade
Request Body:
{
"scores": [
{ "criteria": "Functionality", "points": 38 },
{ "criteria": "Code Quality", "points": 28 },
{ "criteria": "UI/UX", "points": 18 },
{ "criteria": "Documentation", "points": 9 }
],
"total_score": 93,
"feedback": "Excellent work! Consider adding error handling for edge cases."
}
Webhooks
Subscribe to University Block events for real-time notifications.
Available Events
| Event | Description |
|---|---|
enrollment.created | User enrolled in a course |
enrollment.completed | User completed a course |
enrollment.expired | Enrollment access expired |
lesson.completed | User completed a lesson |
quiz.submitted | Quiz attempt submitted |
quiz.passed | User passed a quiz |
quiz.failed | User failed a quiz |
certificate.issued | Certificate generated |
assignment.submitted | Assignment submitted |
assignment.graded | Assignment graded |
discussion.created | New discussion thread |
discussion.replied | New reply to discussion |
Webhook Payload Example
{
"event": "enrollment.completed",
"timestamp": "2025-03-20T16:30:00Z",
"data": {
"enrollment_id": "enrollment-xyz",
"user_id": "user-123",
"course_id": "course-abc",
"completed_at": "2025-03-20T16:30:00Z",
"final_score": 92,
"time_spent_hours": 12.5,
"certificate_id": "cert-xyz789"
}
}
Configure Webhooks
POST /v1/university/webhooks
Request Body:
{
"url": "https://your-app.com/webhooks/university",
"events": ["enrollment.completed", "certificate.issued", "quiz.passed"],
"secret": "your-webhook-secret"
}
SDK Examples
JavaScript/TypeScript
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { university: 'https://university.api.us.23blocks.com' },
appId: 'your-app-id',
apiKey: 'your-api-key',
});
// Create a complete course structure
async function createCourse() {
// Create course
const course = await client.university.createCourse({
title: 'React Masterclass',
description: 'From beginner to expert',
status: 'draft'
});
// Add module
const module = await client.university.createModule({
courseId: course.id,
title: 'Introduction',
position: 1
});
// Add lessons
await client.university.createLesson({
moduleId: module.id,
title: 'Welcome',
contentType: 'video',
content: {
videoUrl: 'https://cdn.example.com/welcome.mp4',
durationSeconds: 300
}
});
// Add quiz
await client.university.createQuiz({
lessonId: 'lesson-id',
title: 'Module Quiz',
passingScore: 80,
questions: [/* ... */]
});
// Publish course
await client.university.updateCourse(course.id, {
status: 'published'
});
return course;
}
// Track learner progress
async function trackProgress(userId: string, courseId: string) {
// Get detailed progress
const progress = await client.university.getProgress({
userId,
courseId
});
console.log(`Progress: ${progress.overallProgress}%`);
console.log(`Time spent: ${progress.totalTimeSpentMinutes} minutes`);
console.log(`Completed lessons: ${progress.lessonsCompleted.length}`);
return progress;
}
// Issue certificate on completion
async function issueCertificate(userId: string, courseId: string) {
const certificate = await client.university.issueCertificate({
userId,
courseId,
templateId: 'template-001'
});
console.log(`Certificate issued: ${certificate.certificateNumber}`);
console.log(`Verify at: ${certificate.verificationUrl}`);
console.log(`PDF: ${certificate.pdfUrl}`);
return certificate;
}
Python
from blocks_sdk import BlocksClient
client = BlocksClient(
base_url="https://api.23blocks.com",
app_id="your-app-id",
api_key="your-api-key"
)
# Enroll users in bulk
def bulk_enroll(course_id: str, user_ids: list, expires_at: str = None):
result = client.university.bulk_enroll(
course_id=course_id,
user_ids=user_ids,
expires_at=expires_at,
send_notification=True
)
print(f"Enrolled {result['enrolled_count']} users")
return result
# Get course analytics
def get_course_stats(course_id: str):
analytics = client.university.get_analytics(
course_id=course_id,
period="30d",
metrics=["enrollments", "completions", "engagement"]
)
print(f"Enrollments: {analytics['metrics']['enrollments']['total']}")
print(f"Completion rate: {analytics['metrics']['completions']['rate']}%")
print(f"Active learners: {analytics['metrics']['engagement']['active_learners']}")
return analytics
# Export learner data
def export_learner_report(course_id: str):
report = client.university.export_report(
course_id=course_id,
format="csv",
include=["progress", "scores", "time_spent"]
)
return report
SCORM Support
The University Block supports SCORM 1.2 and SCORM 2004 packages.
Upload SCORM Package
POST /v1/university/scorm/upload
Request (multipart/form-data):
file: [scorm.zip]
course_id: course-abc
module_id: module-001
title: "Interactive Simulation"
SCORM Runtime API
The block provides full SCORM runtime API support including:
LMSInitialize/InitializeLMSGetValue/GetValueLMSSetValue/SetValueLMSCommit/CommitLMSFinish/TerminateLMSGetLastError/GetLastError
Rate Limits
| Endpoint | Rate Limit |
|---|---|
| Course CRUD | 100 requests/minute |
| Enrollment operations | 200 requests/minute |
| Progress updates | 500 requests/minute |
| Quiz submissions | 50 requests/minute |
| Analytics queries | 30 requests/minute |
| Certificate generation | 20 requests/minute |
Error Handling
All errors follow JSON:API error format:
{
"errors": [
{
"status": "422",
"code": "enrollment_limit_reached",
"title": "Enrollment Limit Reached",
"detail": "This course has reached its maximum enrollment capacity of 500 students.",
"source": { "pointer": "/data/attributes/course_id" }
}
]
}
Common Error Codes
| Code | Description |
|---|---|
course_not_found | Course does not exist |
already_enrolled | User is already enrolled in the course |
enrollment_expired | Enrollment has expired |
prerequisite_not_met | Required prerequisites not completed |
quiz_attempts_exhausted | No more quiz attempts available |
lesson_locked | Lesson is locked (drip content or prerequisites) |
assignment_past_due | Assignment submission deadline passed |