> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hawkings.education/llms.txt
> Use this file to discover all available pages before exploring further.

# Export to SCORM

> Hand a Hawkings cohort to Moodle, Canvas, Cornerstone — anything SCORM-compatible.

SCORM is the universal lingua franca for "I have a course; my customer
has an LMS that isn't mine." Hawkings ships SCORM 1.2 and 2004 zips
that bundle the lesson tree, the content, the activities, and a tiny
runtime that talks back to Hawkings for grading.

## 1. Generate the package

<CodeGroup>
  ```ts TypeScript theme={null}
  const pkg = await hk.scorm.create({
    cohort_id: "coh_123",
    version: "2004",         // or "1.2"
    callback_url: "https://api.hawkings.education/v1/scorm/...",
  });

  console.log(pkg.id, pkg.status);   // pak_..., "pending"
  ```

  ```php PHP theme={null}
  $pkg = $hk->scorm->create([
      'cohort_id' => 'coh_123',
      'version' => '2004',                // or "1.2"
      'callback_url' => 'https://api.hawkings.education/v1/scorm/...',
  ]);

  echo $pkg->id . ' ' . $pkg->status;     // pak_..., "pending"
  ```
</CodeGroup>

Wait for it:

<CodeGroup>
  ```ts TypeScript theme={null}
  const ready = await hk.poll(
    () => hk.scorm.retrieve(pkg.id),
    { until: p => p.status === "ready" },
  );
  ```

  ```php PHP theme={null}
  $ready = $hk->poll(
      fn () => $hk->scorm->retrieve($pkg->id),
      ['until' => fn ($p) => $p->status === 'ready'],
  );
  ```
</CodeGroup>

## 2. Download the zip

<CodeGroup>
  ```ts TypeScript theme={null}
  const stream = await hk.scorm.download(pkg.id);

  // Node:
  import { createWriteStream } from "node:fs";
  const file = createWriteStream("./course.zip");
  stream.pipe(file);

  // Or browser:
  const blob = await stream.blob();
  const url = URL.createObjectURL(blob);
  ```

  ```php PHP theme={null}
  $stream = $hk->scorm->download($pkg->id);

  // Stream straight to disk:
  $file = fopen('./course.zip', 'w');
  while (! $stream->eof()) {
      fwrite($file, $stream->read(8192));
  }
  fclose($file);

  // Or load into memory:
  // $bytes = (string) $stream;
  ```
</CodeGroup>

## 3. Upload to your customer's LMS

Each LMS has its own upload UI. The shape is always:

* Moodle: *Course → Add an activity → SCORM → Upload zip*
* Canvas: *Course → Settings → Apps → SCORM → Add package*
* Cornerstone: *Catalog → Add learning object → SCORM → Upload*

The package boots the Hawkings runtime in an iframe and reports back
to the parent LMS with standard SCORM events: `cmi.completion_status`,
`cmi.score.scaled`, `cmi.session_time`.

## What the package contains

```
course.zip
├── imsmanifest.xml          (SCORM-required)
├── adlcp_rootv1p2.xsd
├── runtime/
│   ├── index.html           (iframe entry point)
│   ├── runtime.js           (talks to Hawkings)
│   └── runtime.css
└── content/
    ├── lessons.json         (the lesson tree)
    └── assets/              (images, audio, …)
```

The `runtime.js` makes signed calls to your Hawkings backend. Students
don't need their own Hawkings login — the LMS' SCORM session is
authenticated as a runtime token under the hood.

## Per-student progress lands back in Hawkings

When a student opens the SCORM package, completes activities, and
submits an assignment, those events flow through `runtime.js` to:

```
POST /v1/scorm/{uuid}/course/{cohort_id}/course-module/{lesson_id}/session
POST /v1/scorm/{uuid}/.../activity-question/evaluate
POST /v1/scorm/{uuid}/.../evaluate
```

In your dashboard you'll see new `Submission`s, identical in shape to
those produced by the native Hawkings student app. The SDK's
`submissions.list({ assignment_id })` reads them transparently.

## Re-issuing a package

Cohorts evolve. To re-publish:

<CodeGroup>
  ```ts TypeScript theme={null}
  await hk.scorm.create({ cohort_id: "coh_123" });
  ```

  ```php PHP theme={null}
  $hk->scorm->create(['cohort_id' => 'coh_123']);
  ```
</CodeGroup>

You'll get a new `id`. Old packages keep working — they continue to
report into the same cohort. Issue a new zip when there's structural
change (lessons added/removed); for content edits the existing zip
fetches the latest from Hawkings on each load.

## Self-hosted runtime

If you don't want the runtime to call `api.hawkings.education`, point
the package at your self-hosted instance:

<CodeGroup>
  ```ts TypeScript theme={null}
  await hk.scorm.create({
    cohort_id: cohortId,
    runtime_base_url: "https://api.your-school.example",
  });
  ```

  ```php PHP theme={null}
  $hk->scorm->create([
      'cohort_id' => $cohortId,
      'runtime_base_url' => 'https://api.your-school.example',
  ]);
  ```
</CodeGroup>

## Limitations

* The package needs network to load lessons. Offline mode is on the
  roadmap — talk to us if you need it sooner.
* AI tutor chat works inside the SCORM iframe with the same token flow
  as the native app.
* Some LMSs cap zip size at 100MB; if your course exceeds that, lessons
  with heavy media are streamed instead of bundled.
