LMS Integration
Connect the SIS to a learning management system: create course shells from sections, keep enrollments in step, and optionally pass final grades back.
To set one up, follow Setting Up LMS Integration. This page describes what the subsystem is and how its pieces relate.
The central idea: a connector is data
An LMS type is data, not code. Canvas, Moodle and D2L Brightspace are not three code paths in the backend; each is a row in lms.connector_types plus a set of versioned operation definitions — URL, headers, body template, response mapping, error mapping, pagination style, rate limits — stored as JSONB and editable in the admin UI.
Two consequences matter in practice:
- Adjusting how an LMS is called (a changed endpoint, a new field, a different error code) is a configuration change, not a release.
- Operations are versioned. A new version is added beside the old one rather than overwriting it, so a change can be previewed before it becomes the one in use.
Three types are seeded: CANVAS (bearer token, Link-header paging, 16 operations), MOODLE (query API key, no paging, 11 operations), and BRIGHTSPACE (D2L OAuth2, bookmark paging, 10 operations). An operation a connector does not define is simply not offered — Moodle's seed has no post_grade, so a Moodle instance is not offered grade passback.
How the pieces fit
| Piece | What it is |
|---|---|
| Connector type | One kind of LMS, with its auth strategy and operation set |
| Instance | One LMS tenant — a production Canvas, a sandbox Canvas, a school's Moodle. Several can run at once |
| Credential | A write-only secret for an instance, sealed with a rotating AES keyring |
| Mappings | SIS term / role / org unit translated into the LMS's own identifiers |
| Assignment rules | Which sections route to which instance, by org unit and campus, by priority |
| Section override | Pins one section to one instance regardless of the rules |
| Sync policy | Global triggers and timings for provisioning, enrollment sync and concluding, plus what drift should do |
| Course shell | The LMS course created for one or more sections |
| Sync jobs | The work queue; SKIP LOCKED, retryable, with a dead-letter status |
| Drift findings | Differences between what the SIS believes and what the LMS reports |
| Export runs | SIS CSV export, for institutions that feed the LMS a file rather than the API |
| Integration log | Every rendered request and its outcome |
Screens
| Screen | Path | Permission |
|---|---|---|
| Instances | /admin/lms/instances | ADMIN.LMS.INSTANCES |
| Connectors | /admin/lms/connectors | ADMIN.LMS.CONNECTORS |
| Sync | /admin/lms/sync | ADMIN.LMS.SECTIONS |
| Logs | /admin/lms/logs | ADMIN.LMS.LOGS |
The sidebar is built from core.permission_nodes, so a screen you have no grant on is absent rather than disabled.
Two things that surprise people
A failed credential save is a missing keyring. Without SIS_LMS_CRED_KEYS and SIS_LMS_CRED_ACTIVE_KEY on the server, configuration screens still work but any path touching a secret returns 503. It is not a duplicate-credential conflict.
Grade passback fails closed. A final grade is published only when its latest change is Colleague-sourced or a registrar release covers it (records.grade_releases). If the release gate is not wired, passback holds everything back rather than publishing ungated grades. Enabling passback does not replay history either — the first scan only initialises the instance's watermark.
What replaced what
This subsystem replaced an earlier one wholesale. Migration 199 created the lms schema; 202 dropped the legacy records.lms_platforms, lms_platform_urls, lms_platform_campuses, lms_credentials, lms_user_tokens, lms_sync_log and lms_enrollment_mapping tables along with four lookup_lms_* tables. The old subsystem stored API secrets in plaintext despite a migration comment claiming otherwise, and could not describe an LMS it had not been coded for.
If you find documentation, a screen or a script referring to "LMS platforms", it predates this and does not apply.
Related
- Setting Up LMS Integration — the step-by-step guide
- LMS Integration Design — schema and engine internals
- LMS API Reference — all 54 endpoints