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: One URL, one environment. All shared-platform API keys are live keys:
pk_live_*/sk_live_*→ your production tenant. Need a separate staging environment? It is available as a paid add-on on deployments in your own cloud account — see 23blocks in a Box.
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', // Your live API key (pk_live_*)
});
// 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 |
Next Steps
Use This Block with AI Agents
This block ships an official Claude Code plugin from the public 23blocks marketplace (19 plugins, 205 skills). Your agent gets full working knowledge of this API — endpoints, identity registration, and conventions.
# one-time: add the 23blocks marketplace
claude plugin marketplace add https://github.com/23blocks-OS/ai-agents
# install this block's plugin
claude plugin install university-block
Configure BLOCKS_API_URL, BLOCKS_API_KEY, and BLOCKS_AUTH_TOKEN. Register identity first: each block is autonomous — register the user with this block's identity endpoint before private calls (only user_unique_id is required; email/phone are optional notification-routing fields).
Full reference: AI Agents & Claude Code Plugins · github.com/23blocks-OS/ai-agents