Skip to content

Building a course

Courses are SCORM-compatible packages (SCORM 1.2/2004, AICC, xAPI, cmi5, or PDF). PlusPlus delegates hosting and progress tracking to SCORM Cloud, so every package upload kicks off an asynchronous import. The upload uses the same three-call pattern as videos and guides — with one SCORM-specific twist: the package is sent as multipart/form-data to a presigned S3 URL.

Flow at a glance

sequenceDiagram
    participant Your app
    participant PlusPlus API
    participant S3 / SCORM Cloud
    Your app->>PlusPlus API: POST /courses/uploads/ { name }
    PlusPlus API-->>Your app: 201 { upload_id, upload_url, upload_fields, course_id }
    Your app->>S3 / SCORM Cloud: POST multipart/form-data (the ZIP)
    loop until ready or errored
        Your app->>PlusPlus API: GET /courses/uploads/{upload_id}/status/
        PlusPlus API-->>Your app: 200 { status }
    end

1. Request an upload URL

Two modes:

  • Create-and-upload — pass name. PlusPlus creates a hidden course and binds an upload URL to it.
  • Re-upload — pass course_id. The new package replaces the active version on that course (SCORM Cloud versions it internally); the course's existing is_hidden is preserved.
curl -X POST https://acme.plusplus.app/api/v2/courses/uploads/ \
  -H "Authorization: Bearer pp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Security Fundamentals", "filename": "security-101.zip" }'
{
  "upload_id": "upl_scorm_xyz",
  "upload_url": "https://uploads.plusplus.app/scorm/…",
  "upload_fields": { "key": "…", "policy": "…", "x-amz-signature": "…" },
  "course_id": "01HXYC0ND9F3K2M6PXJW1ASQTV",
  "status": "awaiting_upload"
}

Unlike the video/guide uploads (a single PUT), the SCORM upload is an S3 presigned POST: the response includes upload_fields you must send alongside the file as form fields.

2. Upload the ZIP as multipart/form-data

Include every key from upload_fields, then the file:

curl -X POST "$UPLOAD_URL" \
  -F "key=$KEY" \
  -F "policy=$POLICY" \
  -F "x-amz-signature=$SIG" \
  -F "file=@./security-101.zip"

The PlusPlus API isn't in the data path — your file goes straight to storage. SCORM Cloud auto-detects the package type from the contents, so filename is only informational.

3. Poll until ready

curl https://acme.plusplus.app/api/v2/courses/uploads/upl_scorm_xyz/status/ \
  -H "Authorization: Bearer pp_your_token_here"
{ "upload_id": "upl_scorm_xyz", "status": "ready" }
status Meaning
awaiting_upload URL issued; storage hasn't received the ZIP.
processing Uploaded; SCORM Cloud is importing (30s to a few minutes).
ready Import complete. SCORM-derived metadata (name, duration, version) is populated.
errored Import failed. Upload a new package to retry.

There's no separate confirm call — PlusPlus detects the upload and triggers the import automatically the first time you poll. Poll every 3–5 seconds.

4. Configure passing and publish

Courses created via upload stay hidden after import — a fresh import lands in draft, and you decide when it goes live. Set passing_score (if you want pass/fail tracking the package doesn't define) and unhide:

curl -X PATCH https://acme.plusplus.app/api/v2/courses/01HXYC0ND9F3K2M6PXJW1ASQTV/ \
  -H "Authorization: Bearer pp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{ "is_hidden": false, "passing_score": 80 }'

Tracking learner progress

Assign the course like any content item (see Assignments & completion). SCORM-specific per-learner outcomes — score, completion, SCORM status — are exposed read-only via Course Registrations:

curl "https://acme.plusplus.app/api/v2/course-registrations/?course_id=01HXYC0ND9F3K2M6PXJW1ASQTV" \
  -H "Authorization: Bearer pp_your_token_here"

Course registrations are read-only. To assign, complete, drop, or exempt a learner, use the Assignments endpoints.

Common pitfalls

  • Using PUT instead of POST for the upload. SCORM uploads are an S3 presigned POST with form fields — not the PUT used for videos/guides. Send every upload_fields entry.
  • Expecting the course to auto-publish. It stays hidden after import. Flip is_hidden to false when ready.
  • Size limits. The default tenant cap is 900 MB (SCORM Cloud trial accounts cap at 100 MB). Larger packages take longer to import.
  • Batch creation. Create empty course records first, then attach packages in a second pass using course_id mode.