Skip to content

@memberjunction/actions-bizapps-lms

Learning Management System (LMS) integration actions for MemberJunction. This package provides a standardized, multi-provider interface for interacting with LMS platforms through the MemberJunction Actions framework, currently supporting LearnWorlds with an extensible architecture for additional providers.

For general Actions framework concepts, patterns, and the “Actions as boundaries” philosophy, see the Actions README and Actions CLAUDE.md. For shared BizApps patterns (credential management, provider base classes, multi-tenant support), see the BizApps README.

The package follows a three-tier class hierarchy: a domain-level base class providing common LMS utilities, provider-specific base classes handling API authentication and communication, and individual action classes implementing specific operations.

graph TD
    subgraph Framework["MemberJunction Actions Framework"]
        BA[BaseAction<br/>@memberjunction/actions]
    end

    subgraph Domain["Domain Layer"]
        BLMS[BaseLMSAction<br/>Credential management<br/>Progress calculation<br/>Duration formatting<br/>Enrollment status mapping]
    end

    subgraph Provider["Provider Layer"]
        LWBase[LearnWorldsBaseAction<br/>API v2 authentication<br/>Paginated requests<br/>Date format conversion<br/>Status mapping]
    end

    subgraph Actions["LearnWorlds Actions"]
        direction LR
        UM["User Management<br/>GetUsers | GetUserDetails<br/>CreateUser | GetUserProgress"]
        CM["Course Management<br/>GetCourses | GetCourseDetails<br/>GetCourseAnalytics"]
        EM["Enrollment Management<br/>EnrollUser | GetUserEnrollments<br/>UpdateUserProgress"]
        AM["Assessment & Certs<br/>GetQuizResults<br/>GetCertificates"]
    end

    BA --> BLMS
    BLMS --> LWBase
    LWBase --> UM
    LWBase --> CM
    LWBase --> EM
    LWBase --> AM

    style Framework fill:#64748b,stroke:#475569,color:#fff
    style Domain fill:#2d6a9f,stroke:#1a4971,color:#fff
    style Provider fill:#7c5295,stroke:#563a6b,color:#fff
    style Actions fill:#2d8659,stroke:#1a5c3a,color:#fff

All actions resolve API credentials through a consistent two-step process, checking environment variables first with a database fallback.

flowchart LR
    A[Action invoked<br/>with CompanyID] --> B{Check env var<br/>BIZAPPS_LEARNWORLDS<br/>_CompanyID_API_KEY}
    B -- Found --> D[Use env credentials]
    B -- Not found --> C[Query CompanyIntegration<br/>entity from database]
    C --> E{APIKey or<br/>AccessToken<br/>present?}
    E -- Yes --> F[Use DB credentials]
    E -- No --> G[Throw error:<br/>No credentials found]
    D --> H[Build API request<br/>with Bearer token]
    F --> H

    style A fill:#2d6a9f,stroke:#1a4971,color:#fff
    style B fill:#b8762f,stroke:#8a5722,color:#fff
    style C fill:#7c5295,stroke:#563a6b,color:#fff
    style D fill:#2d8659,stroke:#1a5c3a,color:#fff
    style F fill:#2d8659,stroke:#1a5c3a,color:#fff
    style G fill:#64748b,stroke:#475569,color:#fff
    style H fill:#2d8659,stroke:#1a5c3a,color:#fff
    style E fill:#b8762f,stroke:#8a5722,color:#fff
Terminal window
npm install @memberjunction/actions-bizapps-lms

This package is part of the MemberJunction monorepo. When working within the monorepo, add the dependency to your package’s package.json and run npm install at the repository root.

INSERT INTO Integration (Name, Description, NavigationBaseURL, ClassName)
VALUES ('LearnWorlds', 'LearnWorlds LMS Integration',
'https://api.learnworlds.com', 'LearnWorldsIntegration');
INSERT INTO CompanyIntegration (CompanyID, IntegrationID, ExternalSystemID, IsActive)
VALUES (@CompanyID, @LearnWorldsIntegrationID, @SchoolDomain, 1);
-- ExternalSystemID: Your LearnWorlds school domain (e.g., 'myschool.learnworlds.com')
Terminal window
# Format: BIZAPPS_LEARNWORLDS_{COMPANY_ID}_{CREDENTIAL_TYPE}
BIZAPPS_LEARNWORLDS_12345_API_KEY=lw_api_xxxxxxxxxxxxx
BIZAPPS_LEARNWORLDS_12345_SCHOOL_DOMAIN=myschool.learnworlds.com
ActionClassDescription
Get UsersGetLearnWorldsUsersActionList and search users with role, status, tag, and date filters
Get User DetailsGetLearnWorldsUserDetailsActionComprehensive user profile with enrollments, achievements, and engagement metrics
Get User ProgressGetLearnWorldsUserProgressActionDetailed learning progress across all courses or a specific course, with optional unit/lesson breakdown
Create UserCreateUserActionCreate a new user with optional course enrollments and welcome email
ActionClassDescription
Get CoursesGetLearnWorldsCoursesActionSearch and filter the course catalog by status, category, level, language, price range, and tags
Get Course DetailsGetLearnWorldsCourseDetailsActionFull course information including curriculum modules, instructors, and enrollment statistics
Get Course AnalyticsGetCourseAnalyticsActionPerformance analytics with enrollment trends, engagement metrics, completion rates, and revenue data
ActionClassDescription
Enroll UserEnrollUserActionEnroll a user in a course with optional pricing, start/expiry dates, and notifications
Get User EnrollmentsGetUserEnrollmentsActionList all enrollments for a user with progress, course details, and summary statistics
Update User ProgressUpdateUserProgressActionUpdate progress at the course or individual lesson level with score and time tracking
ActionClassDescription
Get Quiz ResultsGetQuizResultsActionRetrieve quiz/assessment results with question-level details, answers, and accuracy metrics
Get CertificatesGetCertificatesActionFetch earned certificates with download links, verification codes, and expiration status

Abstract base class for all LMS actions. Provides:

  • getCompanyIntegration(companyId, contextUser) - Resolves the CompanyIntegrationEntity for the given company, with internal caching
  • getCredentialFromEnv(companyId, credentialType) - Reads credentials from environment variables using the BIZAPPS_{PROVIDER}_{COMPANY_ID}_{TYPE} pattern
  • getAPICredentials(integration) - Two-step credential resolution (environment variables first, then database)
  • calculateProgressPercentage(completed, total) - Computes progress as a rounded percentage
  • formatDuration(seconds) - Converts seconds to human-readable format (e.g., 2h 15m 30s)
  • mapEnrollmentStatus(status) - Normalizes provider-specific statuses to standard values: active, completed, expired, suspended, unknown

LearnWorlds-specific base class extending BaseLMSAction. Provides:

  • makeLearnWorldsRequest<T>(endpoint, method, body, contextUser) - Authenticated HTTP request to the LearnWorlds v2 API with Bearer token, error parsing, and JSON handling
  • makeLearnWorldsPaginatedRequest<T>(endpoint, params, contextUser) - Auto-paginating wrapper that collects all pages of results, respecting MaxResults limits
  • parseLearnWorldsDate(dateString) - Handles both ISO strings and Unix epoch timestamps (seconds)
  • mapUserStatus(status) - Maps LearnWorlds statuses to active | inactive | suspended
  • mapLearnWorldsEnrollmentStatus(enrollment) - Determines enrollment status from boolean flags (completed, expired, suspended, active)
  • calculateProgress(progressData) - Extracts percentage, completed/total units, and time spent from LearnWorlds progress objects

Every action requires the CompanyID input parameter (inherited from BaseLMSAction). Additional parameters are action-specific, documented in the sections below.

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
SearchTextstringnullSearch by name, email, or username
RolestringnullFilter by role (student, instructor, admin)
StatusstringnullFilter by status (active, inactive, suspended)
TagsstringnullComma-separated tag filter
CreatedAfterstringnullISO date lower bound
CreatedBeforestringnullISO date upper bound
SortBystringcreatedSort field (created, name, email, last_login)
SortOrderstringdescSort direction (asc, desc)
IncludeCourseStatsbooleantrueInclude enrollment statistics per user
MaxResultsnumber100Maximum number of users to return

Output Parameters:

ParameterTypeDescription
UsersLearnWorldsUser[]Array of user objects with profile and course statistics
TotalCountnumberTotal number of users returned
SummaryobjectAggregate statistics including role distribution, most active users, and recent signups

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
UserIDstringrequiredLearnWorlds user ID
IncludeEnrollmentsbooleantrueInclude course enrollment details
IncludeStatsbooleantrueInclude additional learning statistics

Output Parameters:

ParameterTypeDescription
UserDetailsLearnWorldsUserDetailsComplete user profile with learning data, achievements, and account settings
SummaryobjectEngagement overview with learning progress, achievement counts, and recent activity

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
UserIDstringrequiredLearnWorlds user ID
CourseIDstringnullOptional specific course ID (omit for all courses)
IncludeUnitDetailsbooleanfalseInclude module/unit level breakdown
IncludeLessonDetailsbooleanfalseInclude individual lesson progress

Output Parameters:

ParameterTypeDescription
UserProgressUserLearningProgressComprehensive progress data across all courses or a specific course
SummaryobjectOverview with completion rate, current focus courses, and achievement data

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
SearchTextstringnullSearch in title and description
StatusstringnullFilter by status (published, draft, coming_soon)
CategoryIDstringnullFilter by category ID
LevelstringnullFilter by difficulty (beginner, intermediate, advanced)
LanguagestringnullFilter by course language
OnlyFreebooleanfalseShow only free courses
MinPricenumbernullMinimum price filter
MaxPricenumbernullMaximum price filter
TagsstringnullComma-separated tag filter
InstructorIDstringnullFilter by instructor
CreatedAfterstringnullISO date lower bound
CreatedBeforestringnullISO date upper bound
SortBystringcreatedSort field (created, title, price, enrollments)
SortOrderstringdescSort direction
IncludeEnrollmentStatsbooleantrueInclude enrollment data
MaxResultsnumber100Maximum results to return

Output Parameters:

ParameterTypeDescription
CoursesLearnWorldsCourse[]Array of course objects with full details
TotalCountnumberTotal courses returned
SummaryCourseCatalogSummaryCatalog statistics with category/level/language counts, enrollment stats, and pricing

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
CourseIDstringrequiredLearnWorlds course ID
IncludeModulesbooleantrueInclude curriculum structure
IncludeInstructorsbooleantrueInclude instructor profiles
IncludeStatsbooleantrueInclude enrollment and completion statistics

Output Parameters:

ParameterTypeDescription
CourseDetailsobjectFull course information including curriculum modules, instructor profiles, and stats
SummaryobjectKey metrics overview

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
UserIDstringrequiredLearnWorlds user ID
CourseIDstringrequiredCourse to enroll in
Pricenumber0Enrollment price
JustificationstringAPI EnrollmentReason for enrollment
NotifyUserbooleantrueSend enrollment notification email
StartDatestringnullOptional enrollment start date (ISO format)
ExpiryDatestringnullOptional enrollment expiry date (ISO format)

Output Parameters:

ParameterTypeDescription
EnrollmentDetailsobjectEnrollment record with progress tracking and certificate eligibility
SummaryobjectEnrollment summary with user and course names

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
UserIDstringrequiredLearnWorlds user ID
StatusstringnullFilter by enrollment status
IncludeExpiredbooleanfalseInclude expired enrollments
IncludeCourseDetailsbooleantrueInclude course metadata for each enrollment
SortBystringenrolled_atSort field
SortOrderstringdescSort direction
MaxResultsnumber100Maximum results

Output Parameters:

ParameterTypeDescription
EnrollmentsarrayEnrollment records with progress, grade, and certificate data
TotalCountnumberTotal enrollments found
SummaryobjectAggregate statistics by status, average progress, and total time spent

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
UserIDstringrequiredLearnWorlds user ID
CourseIDstringrequiredCourse ID
LessonIDstringnullOptional lesson ID for lesson-level updates
ProgressPercentagenumbernullNew progress percentage
CompletedbooleannullMark as completed
TimeSpentnumbernullAdditional time spent (seconds)
ScorenumbernullScore for quiz/assignment
NotesstringnullProgress notes

Output Parameters:

ParameterTypeDescription
ProgressDetailsobjectPrevious and updated progress state with diff
SummaryobjectChange summary including whether progress increased

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
CourseIDstringrequiredCourse ID
DateFromstringnullAnalytics period start date
DateTostringnullAnalytics period end date
IncludeUserBreakdownbooleanfalseInclude per-user progress distribution
IncludeModuleStatsbooleantrueInclude module/lesson-level statistics
IncludeRevenuebooleantrueInclude revenue analytics

Output Parameters:

ParameterTypeDescription
CourseAnalyticsobjectComprehensive analytics: enrollment trends, progress distribution, engagement metrics, quiz performance, and revenue
SummaryobjectKey metrics and growth/engagement trends

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
UserIDstringnullFilter by user (either UserID or CourseID required)
CourseIDstringnullFilter by course (either UserID or CourseID required)
DateFromstringnullIssued after date
DateTostringnullIssued before date
IncludeDownloadLinksbooleantrueInclude PDF and image download URLs
SortBystringissued_atSort field
SortOrderstringdescSort direction
MaxResultsnumber100Maximum results

Output Parameters:

ParameterTypeDescription
CertificatesarrayCertificate records with user/course info, download links, and verification data
TotalCountnumberTotal certificates found
SummaryobjectActive/expired counts with optional grouping by course or user

Input Parameters:

ParameterTypeDefaultDescription
CompanyIDstringrequiredMemberJunction Company ID
UserIDstringnullFilter by user (at least one of UserID/CourseID/QuizID required)
CourseIDstringnullFilter by course
QuizIDstringnullFilter by specific quiz
IncludeQuestionsbooleantrueInclude question text and metadata
IncludeAnswersbooleantrueInclude user answers with correct/incorrect analysis
PassingOnlybooleanfalseReturn only passing attempts
DateFromstringnullCompleted after date
DateTostringnullCompleted before date
SortBystringcompleted_atSort field
SortOrderstringdescSort direction
MaxResultsnumber100Maximum results

Output Parameters:

ParameterTypeDescription
QuizResultsarrayQuiz attempts with scores, answers, accuracy metrics, and per-question breakdown
TotalCountnumberTotal results found
SummaryobjectPass rate, average score, average duration, and per-quiz breakdown

The package exports several TypeScript interfaces for strong typing:

  • LearnWorldsUser - User profile with status, role, tags, custom fields, and course statistics
  • LearnWorldsUserDetails - Extended user profile with enrollment details, achievements, and account settings
  • UserEnrollment - Individual course enrollment with progress, grade, and certificate data
  • CourseProgress - Detailed progress for a single course including unit-level breakdown
  • UnitProgress - Progress within a course module/section
  • LessonProgress - Individual lesson progress with type-specific fields (video watch time, quiz scores, assignment grades)
  • UserLearningProgress - Aggregated learning progress across all courses with analytics
  • LearnWorldsCourse - Full course metadata including pricing, structure, enrollment data, and learning outcomes
  • CourseCatalogSummary - Catalog-level statistics with category, level, language, enrollment, and price breakdowns

Get Active Students with Course Statistics

Section titled “Get Active Students with Course Statistics”
import { ActionEngineServer } from '@memberjunction/actions';
const engine = ActionEngineServer.Instance;
const result = await engine.RunAction({
ActionName: 'GetLearnWorldsUsersAction',
Params: [
{ Name: 'CompanyID', Value: 'company-123' },
{ Name: 'Role', Value: 'student' },
{ Name: 'Status', Value: 'active' },
{ Name: 'IncludeCourseStats', Value: true },
{ Name: 'SortBy', Value: 'last_login' },
{ Name: 'SortOrder', Value: 'desc' }
],
ContextUser: currentUser
});
if (result.Success) {
const users = result.Params.find(p => p.Name === 'Users')?.Value;
const summary = result.Params.find(p => p.Name === 'Summary')?.Value;
console.log(`Found ${users.length} active students`);
console.log(`Average courses per student: ${summary.averageCoursesPerUser}`);
}
const result = await engine.RunAction({
ActionName: 'GetLearnWorldsUserProgressAction',
Params: [
{ Name: 'CompanyID', Value: 'company-123' },
{ Name: 'UserID', Value: 'user-456' },
{ Name: 'IncludeUnitDetails', Value: true },
{ Name: 'IncludeLessonDetails', Value: true }
],
ContextUser: currentUser
});
if (result.Success) {
const progress = result.Params.find(p => p.Name === 'UserProgress')?.Value;
console.log(`Completed ${progress.coursesCompleted} of ${progress.totalCourses} courses`);
console.log(`Overall progress: ${progress.overallProgressPercentage}%`);
for (const course of progress.courses) {
if (course.status === 'in_progress') {
console.log(` ${course.courseTitle}: ${course.progressPercentage}%`);
}
}
}
const result = await engine.RunAction({
ActionName: 'GetLearnWorldsCoursesAction',
Params: [
{ Name: 'CompanyID', Value: 'company-123' },
{ Name: 'SearchText', Value: 'JavaScript' },
{ Name: 'Status', Value: 'published' },
{ Name: 'Level', Value: 'beginner' },
{ Name: 'MaxPrice', Value: 100 }
],
ContextUser: currentUser
});
if (result.Success) {
const courses = result.Params.find(p => p.Name === 'Courses')?.Value;
for (const course of courses) {
console.log(`${course.title} - $${course.price} (${course.totalEnrollments} students)`);
}
}
const result = await engine.RunAction({
ActionName: 'CreateUserAction',
Params: [
{ Name: 'CompanyID', Value: 'company-123' },
{ Name: 'Email', Value: 'new.student@example.com' },
{ Name: 'FirstName', Value: 'Jane' },
{ Name: 'LastName', Value: 'Smith' },
{ Name: 'Role', Value: 'student' },
{ Name: 'SendWelcomeEmail', Value: true },
{ Name: 'EnrollInCourses', Value: ['course-101', 'course-202'] }
],
ContextUser: currentUser
});
if (result.Success) {
const summary = result.Params.find(p => p.Name === 'Summary')?.Value;
console.log(`Created user: ${summary.email}`);
console.log(`Enrolled in ${summary.coursesEnrolled} of ${summary.totalCoursesRequested} courses`);
}

The package is designed for extensibility. To add support for a new LMS provider:

  1. Create a provider directory: src/providers/{provider-name}/
  2. Create a provider base class extending BaseLMSAction with provider-specific authentication and API communication
  3. Implement individual action classes extending the provider base class
  4. Map provider-specific data formats to the shared interfaces (LearnWorldsUser, CourseProgress, etc.)
  5. Export everything from provider and actions index files
  6. Re-export the new provider from src/index.ts
src/
base/
base-lms.action.ts # Domain base class (shared)
providers/
learnworlds/ # Existing provider
learnworlds-base.action.ts
actions/
get-users.action.ts
...
moodle/ # New provider
moodle-base.action.ts
actions/
get-users.action.ts
...
index.ts
PackagePurpose
@memberjunction/coreUserInfo, Metadata, RunView for entity access
@memberjunction/global@RegisterClass decorator for action discovery
@memberjunction/actions-baseActionParam, ActionResultSimple, RunActionParams types
@memberjunction/actionsBaseAction class that all actions extend
@memberjunction/core-entitiesCompanyIntegrationEntity for credential/integration management
Terminal window
# Build this package
cd packages/Actions/BizApps/LMS
npm run build
# Watch mode for development
npm run watch