---
title: "How to Sync Course Content Between LMS Platforms with Unified's LMS API"
img: https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_sync_course_content_between_lms_platforms_with_unified_lms_api-icon.png
date: 2026-02-19T23:17:00.000Z
tag: Product
description: "Syncing course content between LMS platforms is harder than it sounds."
url: "https://unified.to/blog/how_to_sync_course_content_between_lms_platforms_with_unified_lms_api"
---

# How to Sync Course Content Between LMS Platforms with Unified's LMS API
------
_February 19, 2026_

Syncing course content between LMS platforms is harder than it sounds.


Every LMS models courses and content differently. Some expose full course catalogs and lesson metadata. Others expose only partial structure. Some support module hierarchies. Others flatten everything under a course. And while unified schemas may suggest writable fields, provider-level capabilities often limit what can actually be created or updated.


When teams build 'content sync' logic assuming universal write support, the result is brittle integrations and inconsistent replication.


Unified's [LMS API](/lms) does not invent structure or simulate unsupported writes. It exposes course and content data exactly as the underlying LMS provides it. Syncing course content becomes a disciplined process of:

- Reading and normalizing structure
- Detecting changes via polling
- Propagating updates only where provider write support exists
- Falling back to mirror-only mode when it does not

This guide shows how to sync course content safely and accurately using Unified's LMS API.


## What 'Sync Course Content' Means Here


In this context, syncing content means:

- Ingesting course catalogs and content structures from LMS platforms
- Maintaining a canonical internal representation
- Detecting structural changes over time
- Propagating updates to destination LMS platforms where supported

It does not mean:

- Guaranteed cross-LMS replication
- Universal module hierarchy writes
- Event-driven real-time structural updates

Unified preserves provider capabilities instead of abstracting them away.


## The Unified LMS Content Model


Unified exposes the following core objects relevant to content syncing:


### Course (`LmsCourse`)


Represents a course catalog entry.


Key fields:

- `id`
- `name`
- `description`
- `is_active`
- `languages`
- `categories`
- `media`
- `instructor_ids`

### Content (`LmsContent`)


Represents individual learning items.


Key fields:

- `id`
- `name`
- `description`
- `course_ids`
- `collection_ids`
- `duration_minutes`
- `sort_order`
- `languages`
- `localizations`
- `skills`
- `is_active`

Content may belong to multiple courses and multiple collections, depending on provider support.


### Collection (`LmsCollection`)


Represents module or grouping hierarchy.


Key fields:

- `id`
- `name`
- `parent_id`
- `is_active`

Collections are optional and provider-dependent.


### Class (`LmsClass`)


Represents sections or sessions of a course.


Optional structural layer depending on provider.


## Step 1: Ingest the Source LMS Catalog


Start by fetching courses.


```typescript
async function fetchCourses(connectionId: string) {
  return await sdk.lms.listLmsCourses({
    connectionId,
    limit: 100,
    sort: 'updated_at',
    order: 'asc',
    fields: 'id,name,description,is_active,updated_at',
  });
}
```


## Step 2: Fetch Course Content


Fetch content items linked to each course.


```typescript
async function fetchCourseContent(connectionId: string, courseId: string) {
  return await sdk.lms.listLmsContents({
    connectionId,
    course_id: courseId,
    sort: 'sort_order',
    order: 'asc',
    fields: 'id,name,description,collection_ids,duration_minutes,sort_order,is_active',
  });
}
```


Not all providers support `sort_order` or `collection_ids`. Always build structure dynamically.


## Step 3: Fetch Module Hierarchy (If Supported)


```typescript
async function fetchCollections(connectionId: string, courseId: string) {
  return await sdk.lms.listLmsCollections({
    connectionId,
    course_id: courseId,
    fields: 'id,name,parent_id,is_active',
  });
}
```


Reconstruct module trees using `parent_id`.


If Collections are not supported:

- Treat content as flat under Course.

## Step 4: Build a Canonical Content Tree


Combine Course, Collection, and Content objects into an internal model.


```typescript
function buildContentTree(course, collections, contents) {
  const modules = buildCollectionTree(collections);

  const contentByCollection = {};
  contents.forEach(c => {
    (c.collection_ids ?? []).forEach(colId => {
      if (!contentByCollection[colId]) contentByCollection[colId] = [];
      contentByCollection[colId].push(c);
    });
  });

  return { course, modules, contentByCollection };
}
```


Fallback strategy:

- If no collections → attach contents directly to course.

## Step 5: Detect Structural Changes


LMS integrations do not provide webhook notifications for structural changes.


Use polling:


```typescript
await sdk.lms.listLmsCourses({
  connectionId,
  updated_gte: lastSync,
});
```


Important:

- `updated_gte` support varies by provider.
- Where unsupported, perform periodic full scans.
- Reconcile by `id` and `updated_at`.

Repeat for:

- Content
- Collections
- Classes (if used)

## Step 6: Determine Write Capability


Before propagating content to another LMS:

1. Check provider-level writable field support.
2. Confirm whether:
    - `lms_course_create` / `update` are supported
    - `lms_content_create` / `update` are supported
    - `lms_collection_create` / `update` are supported

Many LMS providers:

- Allow limited Course updates (e.g., `is_active`, `description`)
- Do not support deep content or module creation
- Do not support structural writes

Only propagate content when write support exists. Otherwise operate in mirror-only mode.


## Mirror-Only Mode (Safe Default)


For providers without write support:

- Sync source LMS structure into your internal system
- Use your system as a unified catalog layer
- Do not attempt to replicate content into the destination LMS

This avoids 501 Not Implemented responses and inconsistent state.


## Controlled Propagation Mode (When Supported)


Where provider write support exists:

- Create missing Courses in destination LMS
- Create or update Content items
- Rebuild module relationships (if writable)
- Respect provider field constraints

Always:

- Validate writable fields via Supported Integrations matrix
- Handle 403 (scope errors) and 501 (unsupported operations)

## Recommended Sync Architecture

1. Ingest source LMS structure
2. Normalize to canonical model
3. Detect structural deltas via polling
4. Evaluate destination write capability
5. Propagate only supported fields
6. Fallback to mirror-only when unsupported

This architecture works across heterogeneous LMS ecosystems.


## Provider Variability Reality


Across LMS providers:

- Course read support is common
- Content read support varies
- Collection/module support is inconsistent
- Write support is limited and provider-specific
- No webhook support for structure changes
- Incremental filtering (`updated_gte`) varies by provider

Design for variability.


## Closing Thoughts


Syncing course content across LMS platforms is not about forcing parity between systems. It is about respecting provider capabilities while maintaining a consistent internal representation.


Unified's LMS API gives you:

- A normalized content model
- Consistent parameter semantics
- Polling-based structural sync
- Explicit provider-level support boundaries

With a disciplined mirror-first approach and controlled propagation where supported, you can build reliable cross-LMS content workflows without overclaiming or relying on unsupported behavior.
[→ Start your 30-day free trial](https://app.unified.to/login)


[→ Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified)