When a school or platform already has an LMS, the integration question is rarely "should we replace it." The procurement, training, and change management costs of switching core infrastructure are prohibitive for most districts and online course providers. The more realistic question is: can an adaptive engine layer on top of what already exists without requiring a rip-and-replace?
The technical answer is yes, with caveats. The caveats are the interesting part. We have worked through LTI 1.3 integrations and REST API integrations across several platform environments over the past year, and the friction points are consistent enough that they are worth documenting. This is not a universal integration guide. It is a description of what we ran into and what we did about it.
Lesson 1: LTI 1.3 Handles Launch Fine but Grades Inconsistently
The Learning Tools Interoperability standard, specifically the 1.3 version with its OIDC-based authentication, has become the dominant mechanism for embedding external tools into platforms like Canvas and Moodle. For launching the adaptive session inside an LMS frame, it works reliably. The student clicks the activity link, authenticates through the OIDC flow, and lands in the adaptive environment. That part is solid.
The problem shows up when writing grades back to the LMS gradebook. LTI 1.3 includes the Assignment and Grade Services (AGS) specification for this purpose. In theory, when a student completes an adaptive session, the engine posts a score to the LMS line item via the AGS endpoint. In practice, the behavior of that endpoint varies significantly across LMS implementations. Some platforms accept scores in any numeric range and normalize them. Others require scores to fall within a declared maximum defined at tool provisioning time. Some implementations have rate limits on AGS writes that are not documented in their developer guides. We hit all three of these in our first two integrations.
The fix we landed on was to treat the LMS gradebook as a one-way reporting destination, not as a real-time sync target. Rather than writing a grade after every session, we batch the grade writes at daily intervals and include retry logic for failed writes. This means a student's grade in the LMS might lag by up to 24 hours compared to the adaptive engine's state, but it eliminated the reliability issues we were seeing with synchronous grade writes. For most use cases in a semester-based curriculum, a 24-hour grade lag is acceptable.
Lesson 2: Roster Sync Is More Fragile Than the Clever/Classlink Marketing Suggests
For K-12 deployments, Clever and Classlink are the dominant rostering middleware solutions. Both provide a standardized API for syncing student, teacher, and section data from the district's student information system into third-party applications. The APIs are well-documented and the concept is straightforward.
The reality is that roster quality varies substantially across districts and changes unpredictably during the school year. Student transfers in and out of sections are not always reflected in the SIS in real time. Some districts have fields in their SIS that they do not populate consistently, which means the data you receive through Clever or Classlink for those fields is either missing or defaults to a placeholder value. Grade level fields, in particular, are populated inconsistently in older SIS installations.
We now treat roster data as a starting point that requires validation, not as ground truth. When a student appears in our system through a roster sync that is missing an expected field (grade level, section assignment), we flag the record as incomplete and surface it in a validation queue for the teacher or admin to resolve. We do not attempt to infer missing fields from other data. That inference creates downstream errors that are difficult to trace.
We are also careful about delete handling. When a student is removed from a roster sync, we do not delete their learning history. We mark the student as inactive in that program. This protects against data loss from sync errors, which happen more often than the middleware documentation implies.
Lesson 3: The Content Graph Cannot Live in the LMS
The logical place to store a course's content graph, from an LMS admin's perspective, is inside the LMS itself. Most LMS platforms have some concept of a content tree, module sequence, or assignment dependency. The temptation is to map the adaptive engine's content graph to these native structures.
We tried this in one integration and abandoned it after three weeks. The problem is that adaptive sequencing requires read and write access to the graph at query time, for every student response, in under 100 milliseconds. LMS content APIs are not designed for that read pattern. They are designed for human browsing and course authoring workflows. The query overhead of going through an LMS API to look up a graph node and its neighbors during a live session is incompatible with responsive question delivery.
Our architecture now keeps the content graph entirely in the adaptive engine's own data layer, with a synchronization step that pulls course structure from the LMS at session start (not during the session). If a teacher adds content to the LMS course after session start, it does not affect the running session, but it syncs into the graph before the next session begins. This introduces a potential one-session lag for new content, which teachers in our pilot programs found acceptable.
Lesson 4: Auth State Between Systems Is Harder to Maintain Than It Looks
A student authenticated into the LMS and then launched into the adaptive engine through LTI has, conceptually, one active session. In practice, they have two: one with the LMS identity provider and one with the adaptive engine's session management. When either session expires or is invalidated, the behavior the student sees can be confusing.
The scenario we saw repeatedly in early deployments: a student leaves a session running in a browser tab, goes to lunch, comes back 45 minutes later, and tries to submit an answer. The LMS session has timed out but the adaptive session has not. The LMS returns a 401 on the grade write, the adaptive engine swallows the error, and the student sees their answer accepted with no visible error. But the grade is not written. From the student's view, everything worked. From the teacher's view, the session never completed.
We addressed this by implementing heartbeat checks from the adaptive engine back to the LMS session endpoint at 10-minute intervals. If the LMS session is no longer valid, the adaptive engine surfaces a "your session has expired, please return to the course" message before the student submits further work. It is a small UX detail but it prevents silent grade write failures, which are the worst kind because they are invisible until a teacher notices a missing grade hours or days later.
What We Would Tell Someone Starting an LMS Integration Today
Spec everything before you build. The LTI 1.3 specification documents behaviors that individual LMS implementations handle differently. Before writing integration code for a specific LMS, read their implementation notes, not just the IMS spec. If they have a sandbox environment, test every API call in the sandbox before touching production. Grade write failures in production are recoverable but disruptive.
Budget for ongoing maintenance. LMS platforms update their authentication flows, revise AGS endpoints, and change rate limits on schedules that are independent of your release cycle. An integration that worked in September may have an edge case failure by December due to an LMS platform update. We have a lightweight integration health check that runs daily against each connected platform and alerts us if expected API responses change behavior. That alert has saved us from silent failures twice in the past six months.
The integration layer is not the product. Every week spent debugging LMS API behavior is a week not spent improving the adaptive engine. We now scope integration work tightly: what is the minimum reliable integration that gets students into adaptive sessions and writes grades back? Everything else, richer grade reporting, bidirectional content sync, parent-facing views, is in a separate roadmap that we prioritize independently. The core integration needs to be stable before the enhancements are worth building.