Generate Your First Report

Generate Your First Report

Learn how to generate reports, view analytics, and export learner progress data.

Table of Contents

Overview

The SCORM API provides comprehensive reporting capabilities to track learner progress, completion rates, scores, and time spent. Reports are available via:

  • Customer Routes (/api/customer/reports/*) - For web application users (Clerk authentication)
  • API Routes (/api/v1/*) - For programmatic access (API key authentication)

Prerequisites

  • API key with read scope (for API routes)
  • OR Clerk authentication (for customer routes)
  • At least one completed session

Report Types

1. Learner Progress Report

Shows completion rates, scores, and time spent per package.

2. Custom Reports

Flexible reports with custom filters, date ranges, and visualizations.

3. Session Reports

Detailed session-level data with CMI information.

Generating Reports

Method 1: Learner Progress Report (Customer Route)

For web application users authenticated via Clerk:

const response = await fetch('/api/customer/reports/learner-progress?range=30d', {
  credentials: 'include', // Include Clerk session cookie
});
const data = await response.json();

Query Parameters:

  • range (optional): Time range
    • 7d - Last 7 days (default)
    • 30d - Last 30 days
    • 90d - Last 90 days
    • all - All time

Response:

{
  "packages": [
    {
      "package_id": "pkg_abc123",
      "package_title": "Introduction to Safety Training",
      "total_sessions": 25,
      "completed_sessions": 18,
      "completion_rate": 72.0,
      "avg_time_spent_seconds": 1250,
      "avg_score": 0.85,
      "passed": 15,
      "failed": 3,
      "unknown": 7
    }
  ],
  "summary": {
    "total_packages": 5,
    "total_sessions": 125,
    "total_completed": 90,
    "overall_completion_rate": 72.0,
    "avg_time_spent_seconds": 1180
  }
}

Method 2: List Sessions (API Route)

For programmatic access with API keys:

curl -X GET "https://scorm-api.allurelms.com/api/v1/sessions?package_id=pkg_abc123&completion_status=completed" \
  -H "X-API-Key: your-api-key-here"

Query Parameters:

  • package_id (optional) - Filter by package
  • user_id (optional) - Filter by user
  • completion_status (optional) - Filter by status (not_attempted, incomplete, completed)
  • success_status (optional) - Filter by success (unknown, passed, failed)
  • page (optional) - Page number (default: 1)
  • limit (optional) - Results per page (default: 20, max: 100)

Response:

{
  "sessions": [
    {
      "id": "session-789",
      "package_id": "pkg_abc123",
      "tenant_id": "tenant-456",
      "user_id": "user-abc",
      "completion_status": "completed",
      "success_status": "passed",
      "score": {
        "scaled": 0.95,
        "raw": 95,
        "min": 0,
        "max": 100
      },
      "attempts": 1,
      "time_spent_seconds": 3600,
      "session_time": "PT1H",
      "created_at": "2025-01-15T00:00:00Z",
      "updated_at": "2025-01-15T01:00:00Z",
      "package": {
        "title": "Course Title",
        "version": "1.2"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 150,
    "total_pages": 8
  }
}

Exporting Data

Export Learner Progress (CSV)

const response = await fetch('/api/customer/reports/learner-progress/export?range=30d', {
  credentials: 'include',
});
const csv = await response.text();

// Download CSV
const blob = new Blob([csv], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'learner-progress.csv';
a.click();

CSV Format:

Package ID,Package Title,Total Sessions,Completed,Completion Rate,Avg Time (seconds),Avg Score,Passed,Failed,Unknown
pkg_abc123,Introduction to Safety Training,25,18,72.0%,1250,0.85,15,3,7

Export Custom Report

const response = await fetch('/api/customer/reports/custom/export?format=csv', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({
    type: 'completion',
    dateFrom: '2025-01-01',
    dateTo: '2025-01-31',
    packageId: 'pkg_abc123'
  })
});
const csv = await response.text();

Formats:

  • csv - Comma-separated values (default)
  • json - JSON format

Custom Reports

Generate flexible reports with custom filters and visualizations:

const response = await fetch('/api/customer/reports/custom', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({
    type: 'completion',
    dateFrom: '2025-01-01',
    dateTo: '2025-01-31',
    packageId: 'pkg_abc123',
    userId: 'user_xyz789',
    chartType: 'bar',
    groupBy: 'day'
  })
});
const report = await response.json();

Request Parameters:

  • type (required): Report type
    • completion - Completion rates
    • time - Time spent
    • score - Scores
  • dateFrom (required): Start date (ISO 8601)
  • dateTo (required): End date (ISO 8601)
  • packageId (optional): Filter by package
  • userId (optional): Filter by user
  • chartType (optional): Visualization type
    • bar - Bar chart
    • line - Line chart
    • table - Data table
  • groupBy (optional): Grouping period
    • day - Daily aggregation
    • week - Weekly aggregation
    • month - Monthly aggregation

Response:

{
  "type": "completion",
  "chartType": "bar",
  "dateRange": {
    "from": "2025-01-01",
    "to": "2025-01-31"
  },
  "rows": [
    ["2025-01-01", 10, 8, "80.0%"],
    ["2025-01-02", 15, 12, "80.0%"],
    ["2025-01-03", 20, 18, "90.0%"]
  ]
}

Common Scenarios

Scenario 1: Display Completion Dashboard

async function loadDashboard() {
  // Get learner progress
  const progressResponse = await fetch('/api/customer/reports/learner-progress?range=30d', {
    credentials: 'include'
  });
  const progress = await progressResponse.json();

  // Display summary
  document.getElementById('total-packages').textContent = progress.summary.total_packages;
  document.getElementById('completion-rate').textContent = 
    `${progress.summary.overall_completion_rate}%`;
  document.getElementById('total-sessions').textContent = progress.summary.total_sessions;

  // Display package list
  const packagesList = document.getElementById('packages');
  progress.packages.forEach(pkg => {
    const item = document.createElement('div');
    item.innerHTML = `
      <h3>${pkg.package_title}</h3>
      <p>Completion: ${pkg.completion_rate}%</p>
      <p>Average Score: ${(pkg.avg_score * 100).toFixed(1)}%</p>
    `;
    packagesList.appendChild(item);
  });
}

Scenario 2: Export Monthly Report

async function exportMonthlyReport(year: number, month: number) {
  const dateFrom = `${year}-${String(month).padStart(2, '0')}-01`;
  const lastDay = new Date(year, month, 0).getDate();
  const dateTo = `${year}-${String(month).padStart(2, '0')}-${lastDay}`;

  const response = await fetch(
    `/api/customer/reports/custom/export?format=csv`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',
      body: JSON.stringify({
        type: 'completion',
        dateFrom,
        dateTo,
        groupBy: 'day'
      })
    }
  );

  const csv = await response.text();
  downloadCSV(csv, `report-${year}-${month}.csv`);
}

Scenario 3: Track Package Performance

async function getPackagePerformance(packageId: string) {
  // Get all sessions for package
  const sessionsResponse = await fetch(
    `/api/v1/sessions?package_id=${packageId}`,
    { headers: { 'X-API-Key': apiKey } }
  );
  const { sessions } = await sessionsResponse.json();

  // Calculate metrics
  const total = sessions.length;
  const completed = sessions.filter(s => s.completion_status === 'completed').length;
  const passed = sessions.filter(s => s.success_status === 'passed').length;
  const avgScore = sessions
    .filter(s => s.score)
    .reduce((sum, s) => sum + (s.score.scaled || 0), 0) / total;
  const avgTime = sessions
    .reduce((sum, s) => sum + (s.time_spent_seconds || 0), 0) / total;

  return {
    total,
    completed,
    completionRate: (completed / total) * 100,
    passed,
    passRate: (passed / completed) * 100,
    avgScore: avgScore * 100,
    avgTimeMinutes: Math.round(avgTime / 60)
  };
}

Troubleshooting

Error: "No Data Available"

Causes:

  • No sessions in the date range
  • Filters too restrictive
  • No completed sessions

Solutions:

  • Expand date range
  • Remove filters
  • Check that sessions exist

Error: "Unauthorized"

Causes:

  • Missing authentication
  • Invalid API key
  • Tenant mismatch

Solutions:

  • Verify authentication
  • Check API key validity
  • Ensure correct tenant

Export Fails

Causes:

  • Large dataset
  • Network timeout
  • Invalid format

Solutions:

  • Use date filters to reduce data
  • Increase timeout
  • Verify format parameter

Best Practices

  1. Use Date Filters: Always filter by date range to improve performance
  2. Cache Results: Cache report data for frequently accessed reports
  3. Paginate Large Datasets: Use pagination for large result sets
  4. Export Asynchronously: For large exports, use async processing
  5. Monitor Performance: Track report generation time and optimize queries

Next Steps


Last Updated: 2025-01-15
Related Documentation: