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

# Update an assigned LMS course

> Publish a corrected immutable LMS version, preview its live impact, and roll it out to existing learners without duplicate To-dos or unrelated progress loss.

OpenTrain keeps published course versions immutable for auditability. That does **not** mean active learners must remain permanently on the first version they received.

The lifecycle has two explicit steps:

1. **Publish** creates a new immutable version. Existing assignments remain unchanged.
2. **Rollout** advances an explicit set of active assignments to that version using their current live state.

<Warning>
  Do not stop after publishing and report that assigned learners cannot receive the update. Do not cancel and recreate their Project To-dos. Use rollout Preview and Apply.
</Warning>

## What rollout preserves

At apply time, OpenTrain re-reads and independently classifies every selected assignment:

* safe assignments advance immediately with compatible lesson progress carried forward;
* a learner actively answering an assessment is queued until the next safe boundary;
* submitted attempts remain pinned to the form and scoring rules under which they were submitted;
* already-current and terminal assignments are skipped idempotently;
* incompatible assignments are reported individually instead of resetting the whole audience;
* no learner assignment or Project To-do is canceled or recreated.

Preview uses the same classifier but is informational. Learners may continue working after Preview; Apply reclassifies them from current state instead of requiring the preview snapshot to remain frozen.

## CLI workflow

The publish JSON response includes `version.id`, `version.contentHash`, `existingAssignments.updated: false`, and a machine-readable `nextAction` that identifies rollout Preview and Apply.

```bash theme={null}
opentrain lms publish \
  --course <course-id> \
  --expected-revision <draft-revision> \
  --confirm-publish \
  --key <publish-idempotency-key> \
  --json

opentrain lms rollout preview \
  --course <course-id> \
  --course-version <new-version-id> \
  --expected-hash <new-content-hash> \
  --job <job-id> \
  --json

opentrain lms rollout \
  --course <course-id> \
  --course-version <new-version-id> \
  --expected-hash <new-content-hash> \
  --job <job-id> \
  --confirm-apply \
  --key <rollout-idempotency-key> \
  --json
```

Use `--assignments <id,id>` instead of or together with `--job` when the employer approves only specific assignments. An organization-wide implicit audience is not allowed.

Read the durable outcome:

```bash theme={null}
opentrain lms rollout status \
  --course <course-id> \
  --course-version <new-version-id> \
  --job <job-id> \
  --json

opentrain lms rollout receipt \
  --course <course-id> \
  --op-key <rollout-idempotency-key> \
  --json
```

The receipt reports `updatedNow`, `queued`, `alreadyCurrent`, `skippedTerminal`, `blocked`, and `failed`, plus assignment-level reasons. Reusing the same idempotency key with the same request replays the original receipt. Use a fresh key only when intentionally retrying isolated failed assignments after correcting their cause.

## MCP workflow

Use the same lifecycle through MCP:

1. `opentrain_lms_publish_course`
2. `opentrain_lms_rollout_preview`
3. `opentrain_lms_rollout_course_version`
4. `opentrain_lms_rollout_status` or `opentrain_lms_rollout_receipt`

Every tool returns the canonical API result in structured content. Branch on typed outcome fields rather than parsing the human summary.

## REST workflow

The canonical endpoints are:

| Method | Endpoint                                                       | Purpose                                                          |
| ------ | -------------------------------------------------------------- | ---------------------------------------------------------------- |
| `POST` | `/api/public/v1/lms/courses/{courseId}/publish`                | Create the immutable version and return its rollout next action. |
| `POST` | `/api/public/v1/lms/courses/{courseId}/rollout-preview`        | Read-only, live informational impact.                            |
| `POST` | `/api/public/v1/lms/courses/{courseId}/rollout`                | Confirm and apply to an explicit audience.                       |
| `GET`  | `/api/public/v1/lms/courses/{courseId}/rollout`                | Read latest status without remembering the key.                  |
| `GET`  | `/api/public/v1/lms/courses/{courseId}/rollout/{operationKey}` | Read one durable receipt and its live queued count.              |

See the [course rollout API reference](/docs/developers/api-reference/lms/course-rollouts) for request shapes.

## Completion checklist

Before reporting that the update reached learners:

* confirm the target immutable version ID and content hash;
* review the exact job or assignment audience;
* inspect blocked and failed assignment-level reasons;
* confirm eligible learners moved to the new effective release;
* monitor queued learners until they cross a safe boundary;
* verify no submitted attempt was regraded and no duplicate Project To-do appeared.
