LMS Integration

RTOPilot can be wired up to a Learning Management System so that creating or transferring a student in RTOPilot automatically creates the matching enrolment in the LMS, and so that course progress in the LMS flows back into RTOPilot's marking and certificates.

Written By Manning Blackall

Last updated 28 days ago

There are two integration modes:

  • Slate - you're using Slate, XMB's first-party LMS. Setup is automatic; RTOPilot provisions a Slate provider for you and the two systems are pre-wired.

  • Custom LMS - you're using any other LMS. You configure HTTP endpoints on your LMS that RTOPilot will call for each operation, with template variables for the dynamic parts.

Both modes are configured under Developers β†’ LMS Integration in the admin portal.

How it fits together

The integration is two-way:

  1. Outbound: RTOPilot β†’ LMS. When something happens in RTOPilot (enrolment created, transferred, cancelled, marked) RTOPilot calls the LMS so the LMS state stays in sync.

  2. Inbound: LMS β†’ RTOPilot. When a student progresses through course work in the LMS, the LMS calls RTOPilot's PUT /api/enrolments/lms-progress endpoint with the new progress percentage. RTOPilot updates marking and certificates accordingly.

The inbound side is the same for both Slate and custom LMS integrations - see the Public API doc for details on authentication and the endpoint shape.

Slate mode

To enable Slate, toggle Use Slate on in the LMS Integration page. On first enable, RTOPilot calls Slate to provision a provider for your organisation; once that succeeds, the integration is ready.

You'll see an Open Slate button on the LMS Integration page that launches the Slate admin dashboard for your provider in a new tab, authenticated as your current user.

You don't need to configure any URLs, bodies, or auth - Slate's endpoints are baked into RTOPilot.

Custom LMS mode

Custom mode is the right choice for any LMS that isn't Slate. RTOPilot treats your LMS as a black box of HTTP endpoints; you tell it which URL to call for each operation and what body to send.

Endpoints to configure

  • Get LMS Courses (required) - GET returning a list of courses (used in pickers when setting up a new RTOPilot course).

  • Get LMS Course (required) - GET for a single course by ${courseId}.

  • Get LMS Enrolment (required) - GET for a single enrolment by ${enrolmentId}.

  • Create LMS Enrolment (required) - POST that creates the enrolment in the LMS. Must return the new LMS enrolment ID, which RTOPilot stores against the RTOPilot enrolment.

  • Transfer LMS Enrolment (optional) - POST that re-assigns an enrolment to a different student. If left blank, transfers in RTOPilot don't propagate to the LMS.

  • Cancel LMS Enrolment (optional) - POST that cancels the enrolment in the LMS.

  • Update LMS Enrolment Marking (optional) - POST that pushes RTOPilot-side marking back into the LMS.

For every endpoint you can configure:

  • The URL, with template variables for dynamic parts.

  • For POST endpoints, the request body as a JSON template.

  • Per-endpoint authentication - same options as webhook auth (NONE, BASIC, BEARER, CUSTOM_HEADER).

Template variables

URL and body templates use ${...} interpolation. Available variables depend on the operation:

  • Get Courses: ${searchTerm}

  • Get Course: ${courseId}

  • Get Enrolment: ${enrolmentId}

  • Create Enrolment: ${courseId}, ${student.id}, ${student.firstName}, ${student.lastName}

  • Transfer Enrolment: ${enrolmentId}, ${to.id}, ${to.firstName}, ${to.lastName}

  • Cancel Enrolment: ${enrolmentId}

  • Update Marking: ${enrolmentId}, ${quizResults}

String values interpolated into JSON bodies are automatically escaped (quotes and backslashes), so it's safe to include user-entered data like names.

${quizResults} is the exception - it interpolates a full JSON array, not a string. Use it without surrounding quotes:

{ "quizResults": ${quizResults} } 

Expected request and response shapes

RTOPilot validates each LMS response against a schema. If the LMS returns a non-2xx status or a body that doesn't match, RTOPilot raises a server error and the operation fails.

Summary of what's expected:

  • Get Courses - array of { id, name }.

  • Get Course - single course object.

  • Get Enrolment - single enrolment object.

  • Create Enrolment - must include the new enrolment's ID so RTOPilot can store it as lmsEnrolmentId.

  • Transfer Enrolment - must include the resulting enrolment ID.

  • Update Marking - acknowledgement; the response is recorded but not re-imported into RTOPilot.

  • Cancel Enrolment - no body required.

If you're integrating with an LMS whose API doesn't match these shapes out of the box, put an adapter (a thin proxy service) in front of it and point RTOPilot at the adapter.

Worked example

For an LMS at https://lms.example.com whose API uses path parameters and a bearer token, the configuration might look like:

  • Get Courses URL: https://lms.example.com/api/courses?q=${searchTerm}

  • Get Course URL: https://lms.example.com/api/courses/${courseId}

  • Get Enrolment URL: https://lms.example.com/api/enrolments/${enrolmentId}

  • Create Enrolment URL: https://lms.example.com/api/enrolments

  • Create Enrolment Body:

    {
      "courseId": "${courseId}",
      "studentFirstName": "${student.firstName}",
      "studentLastName": "${student.lastName}"
    }
  • Cancel Enrolment URL: https://lms.example.com/api/enrolments/${enrolmentId}/cancel

  • Auth (per endpoint): BEARER with your LMS service token.

Pushing progress back to RTOPilot

When a student completes work in the LMS, your LMS should call RTOPilot to update progress. This uses the same RTOPilot API key flow as any other public-API caller - see the Public API doc for full details.

Briefly:

PUT /api/enrolments/lms-progress
Authorization: Bearer rtop_<your-api-key>
Content-Type: application/json
{
  "lmsEnrolmentId": "<id-stored-by-RTOPilot-on-create>",
  "progress": 75
}

The lmsEnrolmentId you send must match the value RTOPilot stored when it called your Create LMS Enrolment endpoint (i.e. the ID your LMS returned in that response).

Operational notes

  • Failures are visible at runtime, not at config time. Saving the LMS config doesn't validate that your endpoints actually work - the first real call (e.g. creating an enrolment) is when you'll find out. Test by creating an enrolment in a non-production environment after configuring.

  • Per-endpoint auth. Each endpoint has its own auth config. In practice you'll usually use the same credentials across all of them, but they're separate so you can use different credentials if your LMS requires it.

  • No retries on outbound LMS calls. Unlike webhooks, calls from RTOPilot to your LMS are not automatically retried. A failed call surfaces as an error to the user performing the action; they can retry by repeating the action.