Upload Your First SCORM Package

Upload Your First SCORM Package

Learn how to upload, validate, and manage SCORM packages with the AllureLMS SCORM API.

Table of Contents

Overview

The SCORM API supports uploading SCORM 1.2 and SCORM 2004 packages. Once uploaded, packages are:

  • Validated for SCORM compliance
  • Extracted and stored securely
  • Made available for launching sessions
  • Tracked with version history

Prerequisites

  • API key with write scope
  • A valid SCORM 1.2 or SCORM 2004 package (ZIP file)
  • Your tenant ID

Package Requirements

Supported Formats

  • SCORM 1.2 - Full support
  • SCORM 2004 (1st-4th Edition) - Full support
  • ZIP Archive - Must be a valid ZIP file

Package Structure

Your SCORM package must contain:

  1. imsmanifest.xml - SCORM manifest file (required)
  2. Content Files - HTML, JavaScript, media files, etc.
  3. Valid Structure - Files organized according to SCORM specification

File Size Limits

  • Default: 100MB per package
  • Large Packages: Use multipart upload for packages >100MB
  • Maximum: 10GB (with multipart upload)

Validation

The API automatically validates:

  • Manifest structure and syntax
  • SCORM version compatibility
  • Required files presence
  • Package integrity

Upload Methods

Method 1: Simple Upload (Recommended for <100MB)

Use this for most packages:

curl -X POST https://scorm-api.allurelms.com/api/v1/packages \
  -H "X-API-Key: your-api-key-here" \
  -F "file=@course.zip" \
  -F "tenant_id=550e8400-e29b-41d4-a716-446655440000" \
  -F "uploaded_by=user-123"

Form Fields:

  • file (required) - The SCORM package ZIP file
  • tenant_id (required) - Your tenant UUID
  • uploaded_by (required) - User identifier (any string up to 255 chars)
  • package_id (optional) - Existing package UUID to upload a new version

Method 2: Multipart Upload (For Large Packages >100MB)

For large packages, use the multipart upload flow:

Step 1: Initialize Upload

curl -X POST https://scorm-api.allurelms.com/api/v1/packages/multipart/init \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
    "uploaded_by": "user-123",
    "filename": "large-course.zip"
  }'

Response:

{
  "upload_id": "upload_abc123",
  "storage_path": "tenant/uploads/tmp_123.zip",
  "part_size": 5242880,
  "total_parts": 20
}

Step 2: Get Presigned URLs for Each Part

curl -X POST https://scorm-api.allurelms.com/api/v1/packages/multipart/part-url \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_id": "upload_abc123",
    "part_number": 1
  }'

Response:

{
  "part_number": 1,
  "url": "https://storage.example.com/upload?presigned=...",
  "expires_in": 3600
}

Step 3: Upload Each Part

curl -X PUT "https://storage.example.com/upload?presigned=..." \
  -H "Content-Type: application/zip" \
  --data-binary @part1.zip

Save the ETag from the response header.

Step 4: Complete Upload

curl -X POST https://scorm-api.allurelms.com/api/v1/packages/multipart/complete \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_id": "upload_abc123",
    "parts": [
      { "part_number": 1, "etag": "\"etag1\"" },
      { "part_number": 2, "etag": "\"etag2\"" }
    ]
  }'

Step 5: Process Package

curl -X POST https://scorm-api.allurelms.com/api/v1/packages/process \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
    "uploaded_by": "user-123",
    "storage_path": "tenant/uploads/tmp_123.zip",
    "original_filename": "large-course.zip"
  }'

Method 3: Validation Only

Test package validity without processing:

curl -X POST https://scorm-api.allurelms.com/api/v1/packages/process \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
    "uploaded_by": "user-123",
    "storage_path": "tenant/uploads/tmp_123.zip",
    "original_filename": "course.zip",
    "validate_only": true
  }'

Response:

{
  "validation_only": true,
  "manifest": {
    "title": "Intro Course",
    "version": "1.2",
    "launch_url": "index.html",
    "sco_count": 5
  },
  "file_size_bytes": 1421132,
  "storage_path": "tenant/uploads/tmp_123.zip"
}

Understanding the Response

Successful Upload Response

{
  "id": "pkg_abc123",
  "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Introduction to Safety Training",
  "version": "1.2",
  "scorm_version": "1.2",
  "launch_url": "index.html",
  "manifest_url": "imsmanifest.xml",
  "storage_path": "tenant-550e8400/packages/pkg_abc123",
  "file_size_bytes": 5242880,
  "metadata": {
    "identifier": "com.example.course.001",
    "schema": "ADL SCORM",
    "schemaversion": "1.2",
    "description": "Course description",
    "sco_count": 5
  },
  "created_at": "2025-01-15T10:30:00.000Z",
  "updated_at": "2025-01-15T10:30:00.000Z"
}

Key Fields Explained

  • id: Unique package identifier (use for launching sessions)
  • title: Extracted from manifest
  • version: SCORM version (1.2 or 2004)
  • launch_url: Entry point for the course
  • storage_path: Internal storage location
  • metadata: Additional package information

Package Management

List All Packages

curl -X GET "https://scorm-api.allurelms.com/api/v1/packages?tenant_id=550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: your-api-key-here"

Get Package Details

curl -X GET https://scorm-api.allurelms.com/api/v1/packages/pkg_abc123 \
  -H "X-API-Key: your-api-key-here"

Update Package Metadata

curl -X PATCH https://scorm-api.allurelms.com/api/v1/packages/pkg_abc123 \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
    "description": "Updated course description",
    "duration": "45m",
    "tags": ["onboarding", "2025"],
    "custom_metadata": { "level": "advanced" }
  }'

Upload New Version

To update a package while keeping the same ID:

curl -X POST https://scorm-api.allurelms.com/api/v1/packages \
  -H "X-API-Key: your-api-key-here" \
  -F "file=@updated-course.zip" \
  -F "tenant_id=550e8400-e29b-41d4-a716-446655440000" \
  -F "uploaded_by=user-123" \
  -F "package_id=pkg_abc123"

The launch URL stays stable, but current_revision increments.

Get Version History

curl -X GET "https://scorm-api.allurelms.com/api/v1/packages/pkg_abc123/versions?tenant_id=550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: your-api-key-here"

Common Scenarios

Scenario 1: Upload and Launch Immediately

// 1. Upload package
const uploadResponse = await fetch('/api/v1/packages', {
  method: 'POST',
  headers: { 'X-API-Key': apiKey },
  body: formData
});
const package = await uploadResponse.json();

// 2. Launch session
const launchResponse = await fetch(`/api/v1/packages/${package.id}/launch`, {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    user_id: 'user-123',
    session_id: crypto.randomUUID()
  })
});
const { player_url } = await launchResponse.json();

// 3. Embed in iframe
document.getElementById('player').src = player_url;

Scenario 2: Batch Upload Multiple Packages

const packages = ['course1.zip', 'course2.zip', 'course3.zip'];

for (const packageFile of packages) {
  const formData = new FormData();
  formData.append('file', packageFile);
  formData.append('tenant_id', tenantId);
  formData.append('uploaded_by', userId);

  const response = await fetch('/api/v1/packages', {
    method: 'POST',
    headers: { 'X-API-Key': apiKey },
    body: formData
  });

  if (response.ok) {
    const pkg = await response.json();
    console.log(`Uploaded: ${pkg.title} (${pkg.id})`);
  }
}

Scenario 3: Validate Before Uploading

// 1. Upload to temporary storage first
const tempUpload = await uploadToTempStorage(file);

// 2. Validate without processing
const validateResponse = await fetch('/api/v1/packages/process', {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    tenant_id: tenantId,
    uploaded_by: userId,
    storage_path: tempUpload.storage_path,
    original_filename: file.name,
    validate_only: true
  })
});

const validation = await validateResponse.json();

// 3. If valid, process for real
if (validation.manifest) {
  await fetch('/api/v1/packages/process', {
    method: 'POST',
    headers: {
      'X-API-Key': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      tenant_id: tenantId,
      uploaded_by: userId,
      storage_path: tempUpload.storage_path,
      original_filename: file.name,
      validate_only: false
    })
  });
}

Troubleshooting

Error: "Invalid SCORM Package"

Causes:

  • Missing imsmanifest.xml
  • Invalid manifest structure
  • Unsupported SCORM version

Solutions:

Error: "File Too Large"

Causes:

  • Package exceeds size limits
  • Quota exceeded

Solutions:

  • Use multipart upload for large packages
  • Check your storage quota
  • Contact support to increase limits

Error: "Package Processing Failed"

Causes:

  • Corrupted ZIP file
  • Missing required files
  • Storage issues

Solutions:

  • Verify ZIP file integrity
  • Re-export package from authoring tool
  • Check storage backend status
  • Review error logs for details

Error: "Quota Exceeded"

Causes:

  • Package limit reached
  • Storage limit reached

Solutions:

  • Delete unused packages
  • Upgrade subscription plan
  • Contact support for quota increase

Best Practices

  1. Validate Before Processing: Use validate_only: true to check packages before committing
  2. Use Versioning: Upload new versions to existing packages to maintain stable IDs
  3. Monitor Quotas: Track your package and storage usage
  4. Handle Errors: Implement retry logic for transient failures
  5. Use Webhooks: Subscribe to package.processing.completed events for async workflows

Next Steps


Last Updated: 2025-01-15
Related Documentation: