Launch Your First SCORM Session

Launch Your First SCORM Session

Learn how to create learning sessions, launch SCORM players, and track learner progress.

Table of Contents

Overview

A SCORM session represents a learner's interaction with a SCORM package. Sessions track:

  • CMI Data: Progress, scores, completion status
  • Time Spent: Total learning time
  • Attempts: Number of times the course was accessed
  • State: Current learning state (incomplete, completed, passed, failed)

Prerequisites

  • API key with write scope
  • A successfully uploaded SCORM package
  • A user ID (your system's learner identifier)

Creating a Session

Method 1: Launch Endpoint (Recommended)

The launch endpoint creates a session and returns a player URL in one call:

curl -X POST https://scorm-api.allurelms.com/api/v1/packages/pkg_abc123/launch \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "user-123",
    "session_id": "session-456"
  }'

Response:

{
  "player_url": "https://scorm-api.allurelms.com/player/session-456",
  "session_id": "session-456",
  "package_id": "pkg_abc123",
  "user_id": "user-123",
  "expires_at": "2025-01-15T22:30:00.000Z"
}

Method 2: External Session (For External Systems)

If you're integrating from an external system (like TrainingOS), use the external session endpoint:

curl -X POST https://scorm-api.allurelms.com/api/v1/sessions/external \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "<external-jwt-token>",
    "package_id": "pkg_abc123",
    "launch_token_ttl_seconds": 600
  }'

Response:

{
  "session_id": "session-456",
  "launch_url": "https://scorm-api.allurelms.com/player/session-456?token=...",
  "launch_token": "eyJhbGciOiJIUzI1NiIs..."
}

Launching the Player

Option 1: Embed in iframe (Recommended)

<iframe
  src="https://scorm-api.allurelms.com/player/session-456"
  width="100%"
  height="800px"
  frameborder="0"
  allow="fullscreen"
  title="SCORM Player"
></iframe>

Option 2: Redirect to Player

window.location.href = 'https://scorm-api.allurelms.com/player/session-456';

Option 3: Open in New Window

window.open(
  'https://scorm-api.allurelms.com/player/session-456',
  'SCORM Player',
  'width=1200,height=800'
);

Tracking Progress

Get Session Data

Retrieve current session state and CMI data:

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

Response:

{
  "id": "session-456",
  "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
  "user_id": "user-123",
  "package_id": "pkg_abc123",
  "cmi_data": {
    "cmi.core.lesson_status": "incomplete",
    "cmi.core.score.raw": "75",
    "cmi.core.score.max": "100",
    "cmi.core.session_time": "PT15M30S"
  },
  "completion_status": "incomplete",
  "success_status": "unknown",
  "score": {
    "scaled": 0.75,
    "raw": 75,
    "max": 100,
    "min": 0
  },
  "time_spent_seconds": 930,
  "version": 3,
  "created_at": "2025-01-15T10:00:00.000Z",
  "updated_at": "2025-01-15T10:15:30.000Z"
}

Polling for Updates

Check session progress periodically:

async function pollSessionProgress(sessionId: string) {
  const interval = setInterval(async () => {
    const response = await fetch(`/api/v1/sessions/${sessionId}`, {
      headers: { 'X-API-Key': apiKey }
    });
    const session = await response.json();

    console.log(`Progress: ${session.completion_status}`);
    console.log(`Score: ${session.score?.scaled || 0}`);

    if (session.completion_status === 'completed') {
      clearInterval(interval);
      console.log('Course completed!');
    }
  }, 5000); // Poll every 5 seconds
}

Updating Session Data

Update CMI Data

The SCORM player automatically updates session data, but you can also update it programmatically:

curl -X PUT https://scorm-api.allurelms.com/api/v1/sessions/session-456 \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "version": 3,
    "cmi_data": {
      "cmi.core.lesson_status": "completed",
      "cmi.core.score.raw": "85",
      "cmi.core.score.max": "100",
      "cmi.core.session_time": "PT20M45S"
    },
    "completion_status": "completed",
    "success_status": "passed",
    "score": {
      "scaled": 0.85,
      "raw": 85,
      "max": 100,
      "min": 0
    },
    "session_time": "PT20M45S"
  }'

Important: Always include the version field for optimistic locking. If you get a 409 Conflict error, fetch the latest session data and retry.

Handling Version Conflicts

async function updateSessionWithRetry(
  sessionId: string,
  updates: any,
  maxRetries = 3
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    // 1. Get current session
    const session = await fetch(`/api/v1/sessions/${sessionId}`, {
      headers: { 'X-API-Key': apiKey }
    }).then(r => r.json());

    // 2. Merge updates
    const payload = {
      version: session.version,
      cmi_data: {
        ...session.cmi_data,
        ...updates.cmi_data
      },
      ...updates
    };

    // 3. Attempt update
    const response = await fetch(`/api/v1/sessions/${sessionId}`, {
      method: 'PUT',
      headers: {
        'X-API-Key': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });

    if (response.ok) {
      return await response.json();
    }

    if (response.status === 409 && attempt < maxRetries - 1) {
      console.log(`Version conflict, retrying... (${attempt + 1}/${maxRetries})`);
      continue;
    }

    throw new Error(`Update failed: ${response.status}`);
  }
}

Common Scenarios

Scenario 1: Launch and Track Completion

// 1. Launch session
const launchResponse = await fetch(`/api/v1/packages/${packageId}/launch`, {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    user_id: userId,
    session_id: sessionId
  })
});
const { player_url, session_id } = await launchResponse.json();

// 2. Embed player
const iframe = document.createElement('iframe');
iframe.src = player_url;
iframe.width = '100%';
iframe.height = '800px';
document.body.appendChild(iframe);

// 3. Poll for completion
const checkCompletion = setInterval(async () => {
  const session = await fetch(`/api/v1/sessions/${session_id}`, {
    headers: { 'X-API-Key': apiKey }
  }).then(r => r.json());

  if (session.completion_status === 'completed') {
    clearInterval(checkCompletion);
    showCompletionMessage(session);
  }
}, 5000);

Scenario 2: Resume Previous Session

// 1. Find existing session
const sessionsResponse = await fetch(
  `/api/v1/sessions?package_id=${packageId}&user_id=${userId}`,
  { headers: { 'X-API-Key': apiKey } }
);
const { sessions } = await sessionsResponse.json();

// 2. Find incomplete session
const incompleteSession = sessions.find(
  s => s.completion_status === 'incomplete'
);

if (incompleteSession) {
  // Resume existing session
  const playerUrl = `https://scorm-api.allurelms.com/player/${incompleteSession.id}`;
  window.location.href = playerUrl;
} else {
  // Create new session
  const launchResponse = await fetch(`/api/v1/packages/${packageId}/launch`, {
    method: 'POST',
    headers: {
      'X-API-Key': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      user_id: userId,
      session_id: crypto.randomUUID()
    })
  });
  const { player_url } = await launchResponse.json();
  window.location.href = player_url;
}

Scenario 3: Track Multiple Users

async function launchForMultipleUsers(packageId: string, userIds: string[]) {
  const sessions = [];

  for (const userId of userIds) {
    const response = await fetch(`/api/v1/packages/${packageId}/launch`, {
      method: 'POST',
      headers: {
        'X-API-Key': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        user_id: userId,
        session_id: crypto.randomUUID()
      })
    });

    if (response.ok) {
      const session = await response.json();
      sessions.push(session);
    }
  }

  return sessions;
}

Troubleshooting

Error: "Session Not Found"

Causes:

  • Invalid session ID
  • Session expired
  • Session belongs to different tenant

Solutions:

  • Verify session ID is correct
  • Check session expiration time
  • Ensure API key matches tenant

Error: "Version Conflict" (409)

Causes:

  • Concurrent updates to same session
  • Using outdated version number

Solutions:

Error: "Package Not Found"

Causes:

  • Invalid package ID
  • Package deleted
  • Package belongs to different tenant

Solutions:

  • Verify package ID
  • Check package exists
  • Ensure API key matches tenant

Player Not Loading

Causes:

  • CORS issues
  • Invalid session token
  • Network connectivity

Solutions:

  • Check browser console for errors
  • Verify CORS configuration
  • Check network connectivity
  • Ensure session hasn't expired

Best Practices

  1. Use Unique Session IDs: Generate UUIDs for session IDs to avoid conflicts
  2. Handle Version Conflicts: Always implement retry logic for session updates
  3. Poll for Progress: Check session status periodically for real-time updates
  4. Resume Sessions: Check for existing incomplete sessions before creating new ones
  5. Monitor Expiration: Track session expiration times and refresh if needed
  6. Error Handling: Implement comprehensive error handling for all API calls

Next Steps


Last Updated: 2025-01-15
Related Documentation: