agentsclimarketplace

Lw lms rest frontend

Skill Lonsdale201/wp-agent-skills/lw-plugins/lw-lms-rest-frontend

A community-maintained collection of agent skills for WordPress plugin and theme development.

Install
npx -y skills add Lonsdale201/wp-agent-skills --skill lw-lms-rest-frontend

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Build a custom frontend against LW LMS's headless `/wp-json/lms/v1` REST API in lw-lms v1.6.0. Use for React, Vue, Astro, mobile, or theme code that calls `/courses`, `/courses/{id}`, `/lessons/{id}`, `/progress`, `/progress/course/{id}`, or `/download/{id}` and must handle public course content, lesson/access gating, paid-course products/subscriptions/subscription_variations/memberships, custom paid-access decisions, progress payloads, downloads, and nonce/app-password auth.

SKILL.md

9.6 KB, as published. Nobody here has run it

LW LMS: REST frontend consumer

For frontend developers consuming LW LMS data: course catalog, course detail, lesson player, progress dashboard, and protected downloads. The core lw-lms plugin ships a headless REST API; it does not ship public templates, shortcodes, or blocks.

BETA NOTICE. The plugin README says the plugin is under active development and not recommended for production use. Snapshot the JSON shapes in tests and review CHANGELOG.md before upgrading. This skill is verified against local lw-lms v1.6.0.

Version deltas that matter

  • v1.6.0: no route or JSON-schema change. A server-side lw_lms_has_course_access callback can now alter the final logged-in paid-course access decision after built-in access checks, which affects access.has_access, lesson accessibility, attachments, lesson detail, progress writes, and protected downloads.
  • v1.5.1: maintenance release, no functional REST changes.
  • v1.5.0: paid-course denied access payload can include memberships when WooCommerce Memberships is active and the course has _lw_lms_membership_plan_ids.
  • v1.4.0: course content in /courses/{id} is public marketing/about content. It is no longer gated behind access.has_access. Lesson content is still gated by /lessons/{id}.
  • v1.4.0: open-course lessons remain accessible to guests even if marked as preview.
  • v1.3.0: first logged-in access to a free course lazily writes a source='free' access row server-side; the REST consumer does not need to do anything special.
  • v1.2.15: denied paid-course access payload can include subscription_variations.

Misconception this skill corrects

"I should use course.content as the access gate."

Wrong for v1.4.0+. CourseTransformer::transform_full() always includes the course content; this is the public course description. Use course.access.has_access and each lesson's accessible flag for gating.

// WRONG: course content is public and is not proof of access.
if (course.content) {
    renderLessonPlayer();
}

// RIGHT: access comes from access.has_access and per-lesson accessible.
if (course.access.has_access) {
    return <CourseContent html={course.content} progress={course.progress} />;
}

return <PurchaseGate access={course.access} />;

Lesson body content still comes from GET /lms/v1/lessons/{id} and returns 403 forbidden when AccessChecker::has_lesson_access() denies the request.

In v1.6.0, a companion plugin may provide the final logged-in paid-course decision through lw_lms_has_course_access. Frontend code should continue to trust the server response; it must not reproduce membership or entitlement rules in JavaScript. Backend callbacks must be deterministic: the full-course transformer checks course access twice, so a changing result can make access.has_access disagree with lesson/attachment gating in one response.

REST API surface

Namespace: lms/v1, mounted at /wp-json/lms/v1/....

MethodPathAuthPurpose
GET/lms/v1/coursespublicPaginated public course list
GET/lms/v1/courses/{id}publicSingle course detail; course content is public, lesson rows carry accessible
GET/lms/v1/lessons/{id}public route, access-gated bodySingle lesson; 403 without lesson access
GET/lms/v1/progresslogged-inCurrent user's all progress rows
GET/lms/v1/progress/course/{id}logged-inCurrent user's progress rows for one course plus course summary progress
POST/lms/v1/progresslogged-in + lesson accessUpsert lesson status
GET/lms/v1/download/{id}public route, access-gated fileStream protected attachment binary

Detailed response contract

Read references/rest-response-contract.md when implementing request parameters, full JSON shapes, nullable fields, lesson errors, progress validation, or binary downloads. The route/auth/access decisions and client safety rules remain in this main skill.

Authentication

ContextAuth pattern
Public catalog and course detailno auth
Logged-in browser UIWordPress cookie + X-WP-Nonce, usually via wp.apiFetch
Headless/mobileApplication Password Basic auth, or a vetted JWT/OAuth layer
Downloadssame auth context as the protected course/lesson

For browser fetch, include both nonce and credentials:

await fetch('/wp-json/lms/v1/progress', {
  method: 'POST',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/json',
    'X-WP-Nonce': wpApiSettings.nonce
  },
  body: JSON.stringify({ course_id: 42, lesson_id: 100, status: 'completed' })
});

Critical rules

  • Course content is public in v1.4.0+; never use it as the access gate.
  • A server-side v1.6.0 paid-access filter may change access without creating an enrollment row; the REST response remains the authority for the client.
  • Lesson detail and download endpoints are the protected surfaces.
  • Iterate both sections and lessons_without_section.
  • Render locked lessons disabled, not hidden.
  • access.memberships is available only when WooCommerce Memberships functions exist and linked plans are configured.
  • List endpoint access info is intentionally small; build purchase gates from the single-course response.
  • course_progress uses completed_lessons, not completed_count.
  • completed_at is null for non-completed rows.
  • content_raw is editor-only source content; do not render it in public UI.
  • download_url is already a full REST URL; do not reconstruct it by concatenating /wp-json.
  • The download endpoint returns binary data; do not call response.json() on it.

Common mistakes

// WRONG: only iterating sections.
course.sections.map(section => <Section section={section} />);

// RIGHT: include orphan lessons too.
<>
  {course.sections.map(section => <Section key={section.id} section={section} />)}
  {course.lessons_without_section.length > 0 && (
    <LessonGroup lessons={course.lessons_without_section} />
  )}
</>
// WRONG: old pre-1.4 assumption.
const canAccess = Boolean(course.content);

// RIGHT.
const canAccess = course.access.has_access;
// WRONG: progress key from stale docs.
<ProgressBar value={data.course_progress.completed_count} />

// RIGHT.
<ProgressBar value={data.course_progress.completed_lessons} />
// WRONG: POST body missing course_id.
fetch('/wp-json/lms/v1/progress', {
  method: 'POST',
  body: JSON.stringify({ lesson_id: 100, status: 'completed' })
});

// RIGHT.
fetch('/wp-json/lms/v1/progress', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings.nonce },
  credentials: 'same-origin',
  body: JSON.stringify({ course_id: 42, lesson_id: 100, status: 'completed' })
});

Cross-references

  • Use lw-lms-backend-extend for hooks, access/progress repositories, and companion-plugin backend integration.
  • Use lw-lms-abilities for admin/agent lw-lms/* Abilities API calls; those are not the learner-facing REST endpoints.
  • Use lw-lms-wp-cli-operations for operational WP-CLI course, lesson, enrollment, revoke, and force-complete commands.
  • Use lw-lms-learndash-migration for the one-time LearnDash migration command.

What this skill does NOT cover

  • A separate frontend plugin. This skill documents the core lw-lms REST contract only.
  • Payment processing. Link to WooCommerce products/subscriptions/membership join URLs and let WooCommerce handle checkout.
  • Custom REST route registration. Use normal WordPress register_rest_route() patterns in a companion plugin.
  • Server-rendered theme templates. You can consume REST server-side, but direct CPT/meta queries are usually simpler in PHP templates.

References

  • REST namespace and route registration: includes/Api/RestApi.php.
  • Course list/detail routes: includes/Api/Controllers/CoursesController.php.
  • Lesson route and 403 behavior: includes/Api/Controllers/LessonsController.php.
  • Progress routes and cross-validation: includes/Api/Controllers/ProgressController.php.
  • Download route and binary response: includes/Api/Controllers/DownloadController.php.
  • Course response shape and public content: includes/Api/Transformers/CourseTransformer.php.
  • Lesson response shape: includes/Api/Transformers/LessonTransformer.php.
  • Progress row shape: includes/Api/Transformers/ProgressTransformer.php.
  • Paid access payload including memberships: includes/Access/AccessChecker.php, includes/Access/MembershipChecker.php.
  • Official documentation: https://github.com/lwplugins/lw-lms
  • Verified source paths:
    • wp-content/plugins/lw-lms/includes/Access/SubscriptionVariationChecker.php
    • wp-content/plugins/lw-lms/includes/Access/WooCommerceChecker.php
    • wp-content/plugins/lw-lms/includes/Meta/VideoParser.php
    • wp-content/plugins/lw-lms/includes/Progress/ProgressCalculator.php
    • wp-content/plugins/lw-lms/CHANGELOG.md

Gives 0 of the 12 instructions most design frontend skills give

Counted across 1,170 of the 1,878 authors here whose files we hold, read 2026-08-06

  • use css variables for color consistencyin 73 of 1170, across 24 files
  • match implementation complexity to the aesthetic visionin 70 of 1170, across 20 files
  • commit to one bold aesthetic direction before codingin 70 of 1170, across 25 files
  • add atmospheric background effects and texturesin 58 of 1170, across 10 files
  • use unexpected spatial compositions and layoutsin 55 of 1170, across 7 files
  • implement real working codein 55 of 1170, across 7 files
  • vary themes and aesthetics across different designsin 48 of 1170, across 7 files
  • launch chromium in headless modein 47 of 1170, across 4 files
  • close the browser when donein 47 of 1170, across 4 files
  • run provided scripts with help flag firstin 47 of 1170, across 4 files
  • use descriptive selectors for elementsin 47 of 1170, across 4 files
  • wait for network idle statein 46 of 1170, across 3 files

Said here and by no other author read

  • gate access using access.has_access
  • use lesson accessible flag for gating
  • iterate sections and lessons_without_section
  • render locked lessons disabled
  • use completed_lessons for progress
  • include course_id and lesson_id in progress posts

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.