learndash-rest-api
Build or audit LearnDash REST API integrations for courses, lessons, topics, quizzes, groups, users, course steps, enrollments, group memberships, progress, and the modern LearnDash REST manifest/OpenAPI docs. Use when code mentions ldlms/v2, learndash/v1, LearnDash_REST_API, lea
Install
npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/learndash/learndash-rest-api
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart
git clone https://github.com/Lonsdale201/wp-agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lonsdale201/wp-agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
LearnDash REST API
Use this when building or reviewing a REST/headless integration for LearnDash. Prefer source-verified routes and schemas over guessed endpoint shapes.
LearnDash 5.1.9 exposes two relevant REST surfaces:
- Classic LearnDash content/relationship endpoints under
ldlms/v2. - Newer manifest/OpenAPI/documentation endpoints under
learndash/v1.
The v2 endpoints are still the practical surface for course, group, user,
progress, and course-steps CRUD. The learndash/v1 manifest/docs surface is the
discovery contract and includes newer endpoint metadata.
When to use this skill
Trigger when ANY of these appears:
/wp-json/ldlms/v2/,/wp-json/learndash/v1/,LearnDash_REST_API,learndash_rest_api_enabled, orlearndash-rest-api-controllers.- REST work around
sfwd-courses,sfwd-lessons,sfwd-topic,sfwd-quiz,groups, users, course progress, quiz progress, or assignments. - Headless app needs course outlines, enrollments, group memberships, or progress data.
- API client gets 401/403, missing endpoint, wrong rest base, or malformed
/stepspayload errors.
Enablement and namespaces
REST availability requires both:
- Constant
LEARNDASH_REST_API_ENABLEDtrue. The default is true. - LearnDash REST settings section value
enabled = yes.
LearnDash_REST_API::enabled() also exposes the
learndash_rest_api_enabled filter.
Classic LearnDash REST namespace defaults to ldlms; v2 routes use
ldlms/v2. The namespace constant is LEARNDASH_REST_API_NAMESPACE.
Do not hard-code every route base. LearnDash settings allow REST base overrides for courses, lessons, topics, quizzes, groups, users, and relation subroutes.
Default important v2 bases:
- Courses:
/wp-json/ldlms/v2/sfwd-courses - Course steps:
/wp-json/ldlms/v2/sfwd-courses/{id}/steps - Course users:
/wp-json/ldlms/v2/sfwd-courses/{id}/users - Course groups:
/wp-json/ldlms/v2/sfwd-courses/{id}/groups - Groups:
/wp-json/ldlms/v2/groups - Group courses:
/wp-json/ldlms/v2/groups/{id}/courses - Group users:
/wp-json/ldlms/v2/groups/{id}/users - Users courses:
/wp-json/ldlms/v2/users/{id}/courses - Users groups:
/wp-json/ldlms/v2/users/{id}/groups - User course progress:
/wp-json/ldlms/v2/users/{id}/course-progress
Discovery workflow
For learndash/v1, do not guess parameters. The local source declares the
manifest as the mandatory discovery workflow.
Use:
GET /wp-json/learndash/v1/manifest- Follow each endpoint
hrefto fetch the exact JSON schema. - Or inspect
GET /wp-json/learndash/v1/docs/openapi.
Most learndash/v1 endpoints are experimental and require:
Learndash-Experimental-Rest-Api: allow
The manifest and OpenAPI documentation endpoints are public and not experimental in 5.1.9.
Course steps endpoint
Creating lesson/topic/quiz posts is not enough to build a course outline. Update the course steps relationship.
Endpoint:
GET/POST /wp-json/ldlms/v2/sfwd-courses/{course_id}/steps
Read permission:
- Anonymous users are denied unless the filter
learndash_rest_course_steps_allow_anonymous_readreturns true. - Admin users can read.
- Other logged-in users are denied by the controller.
Update permission:
- Requires
edit_courses.
Expected update body shape is an object keyed by LearnDash post types. Lesson, topic, and quiz IDs are object keys, not list items:
{
"sfwd-lessons": {
"123": {
"sfwd-topic": {
"456": {
"sfwd-quiz": {
"789": {}
}
}
},
"sfwd-quiz": {}
}
},
"sfwd-quiz": {
"999": {}
}
}
Common bug: sending [123, 456] arrays. The controller expects nested objects
where IDs are keys.
Course custom pagination in 5.1.7+
Course REST fields expose lessons_per_page, lesson_per_page_custom, and
topic_per_page_custom. When custom pagination is enabled, use positive
integers for both custom values. LearnDash 5.1.7+ normalizes empty or zero
values to the corresponding global setting, then to
LEARNDASH_LMS_DEFAULT_WIDGET_PER_PAGE if the global value is also invalid.
Do not use zero to mean "show all" for these course-specific fields. Disable custom pagination when the course should inherit the global values.
Enrollment and relation endpoints
Course users:
GET /ldlms/v2/sfwd-courses/{id}/usersPOST /ldlms/v2/sfwd-courses/{id}/userswithuser_idsDELETE /ldlms/v2/sfwd-courses/{id}/userswithuser_ids
The controller calls ld_update_course_access() for enrollment and rejects open
courses with learndash_rest_rejected_course_open. It can skip admin users
when admin auto-enroll is enabled.
User courses:
GET/POST/DELETE /ldlms/v2/users/{id}/courses- Individual course relation routes can update enrollment dates with
enrolled_at.
Group courses:
GET/POST/DELETE /ldlms/v2/groups/{id}/courseswithcourse_ids.- Mutations call
ld_update_course_group_access().
Group users:
GET/POST/DELETE /ldlms/v2/groups/{id}/userswithuser_ids.- Mutations call
ld_update_group_access().
User groups:
GET/POST/DELETE /ldlms/v2/users/{id}/groupswithgroup_ids.
Permission model:
- Most relation reads/writes require LearnDash admin permissions.
- Group leaders can read users for groups they administer.
- Group leaders are not generally allowed to mutate group users through these controllers.
Extending REST safely
LearnDash's v2 REST loader registers controllers on rest_api_init and exposes
the controller list through learndash-rest-api-controllers. Prefer adding a
separate namespaced WP REST controller for custom app APIs unless the task
specifically needs to extend LearnDash's own route catalog.
For custom fields on LearnDash posts:
- Register real post meta with
show_in_restwhen possible. - For LearnDash metabox settings, inspect
register_rest_fields()in the v2 posts controller and the specific post-type controller before inventing field names. - Keep permission callbacks explicit. Do not expose course progress or enrollment writes publicly.
Progress routes are read-only
The v2 user progress routes expose course headers, ordered steps, quiz
progress, and challenge-exam state through GET requests. In 5.1.9,
/users/{id}/course-progress, its /{course} route, /{course}/steps, and
/{course}/exam do not register a general progress-write method.
Do not invent a PATCH contract from the response schema. For a custom mutation
endpoint, authorize the actor against the target user and call LearnDash's
completion/progress APIs; use learndash-course-progress for that lifecycle.
Headless integration checklist
- Check REST is enabled in constants and LearnDash settings.
- Discover actual bases from settings or manifest/docs instead of assuming defaults on customized sites.
- Authenticate with Application Password, cookie+nonce, OAuth/JWT layer, or a site-specific auth plugin.
- Fetch course posts and then fetch
/stepsfor the outline. - Use relation endpoints for enrollment/group changes, not raw usermeta.
- Use progress endpoints for progress, not direct activity-table writes.
- Respect pagination, batch limits, and per-item success/failure responses.
- Cache read responses carefully and invalidate on post, access, group, and progress mutations.
Common mistakes to reject
- Guessing
learndash/v1request bodies without first reading manifesthrefdetails. - Hard-coding route bases when LearnDash settings can change them.
- Creating lessons/topics but never updating course steps.
- Sending arrays to
/stepsinstead of nested ID-keyed objects. - Trying to enroll users into an open course through the course-users endpoint.
- Assuming group leaders can mutate all group REST relations.
- Exposing a custom public route that returns locked course content without
checking
sfwd_lms_has_access().
Cross-references
- Use
learndash-course-accessfor final access checks and direct course enrollment semantics. - Use
learndash-group-accessfor group membership and group-course relation behavior. - Use
learndash-woocommerce-accessfor Woo-driven access changes. - Use
learndash-course-progressfor server-side completion, reset, activity, and progress-write behavior. - Use
wp-rest-apifor general WordPress REST security and schema patterns.
References
Validated against LearnDash LMS 5.1.9 local source:
includes/rest-api/class-ld-rest-api.phpincludes/settings/settings-sections/class-ld-settings-section-general-rest-api.phpincludes/rest-api/v2/class-ld-rest-courses-steps-controller.phpincludes/rest-api/v2/class-ld-rest-courses-users-controller.phpincludes/rest-api/v2/class-ld-rest-users-courses-controller.phpincludes/rest-api/v2/class-ld-rest-groups-courses-controller.phpincludes/rest-api/v2/class-ld-rest-groups-users-controller.phpincludes/rest-api/v2/class-ld-rest-users-groups-controller.phpincludes/rest-api/v2/class-ld-rest-users-course-progress-controller.phpincludes/settings/settings-metaboxes/class-ld-settings-metabox-course-display-content.phpsrc/Core/Modules/REST/V1/src/Core/Modules/REST/Documentation_Migration/- Official documentation: https://developers.learndash.com/
- Verified source paths:
wp-content/plugins/sfwd-lms/includes/rest-api/v2/class-ld-rest-posts-controller.phpwp-content/plugins/sfwd-lms/includes/rest-api/v2/class-ld-rest-courses-controller.php
Files (wp-agent-skills)
-
agents
-
openai.yaml 196 B
interface: display_name: "LearnDash REST API" short_description: "LearnDash REST endpoints and relation APIs" default_prompt: "Use $learndash-rest-api to plan a LearnDash REST integration."
-
-
SKILL.md 10.1 KB
--- name: learndash-rest-api description: >- Build or audit LearnDash REST API integrations for courses, lessons, topics, quizzes, groups, users, course steps, enrollments, group memberships, progress, and the modern LearnDash REST manifest/OpenAPI docs. Use when code mentions ldlms/v2, learndash/v1, LearnDash_REST_API, learndash_rest_api_enabled, learndash-rest-api-controllers, sfwd-courses REST routes, /steps, /users, /groups, /course-progress, Learndash-Experimental-Rest-Api, or headless LearnDash clients. metadata: wp-skills-author: "Soczó Kristóf" wp-skills-contact: "mailto:lonsdale201@hotmail.com" wp-skills-plugin: "sfwd-lms" wp-skills-plugin-version-tested: "5.1.9" wp-skills-php-min: "7.4" wp-skills-last-updated: "2026-08-06" --- # LearnDash REST API Use this when building or reviewing a REST/headless integration for LearnDash. Prefer source-verified routes and schemas over guessed endpoint shapes. LearnDash 5.1.9 exposes two relevant REST surfaces: - Classic LearnDash content/relationship endpoints under `ldlms/v2`. - Newer manifest/OpenAPI/documentation endpoints under `learndash/v1`. The v2 endpoints are still the practical surface for course, group, user, progress, and course-steps CRUD. The `learndash/v1` manifest/docs surface is the discovery contract and includes newer endpoint metadata. ## When to use this skill Trigger when ANY of these appears: - `/wp-json/ldlms/v2/`, `/wp-json/learndash/v1/`, `LearnDash_REST_API`, `learndash_rest_api_enabled`, or `learndash-rest-api-controllers`. - REST work around `sfwd-courses`, `sfwd-lessons`, `sfwd-topic`, `sfwd-quiz`, `groups`, users, course progress, quiz progress, or assignments. - Headless app needs course outlines, enrollments, group memberships, or progress data. - API client gets 401/403, missing endpoint, wrong rest base, or malformed `/steps` payload errors. ## Enablement and namespaces REST availability requires both: - Constant `LEARNDASH_REST_API_ENABLED` true. The default is true. - LearnDash REST settings section value `enabled = yes`. `LearnDash_REST_API::enabled()` also exposes the `learndash_rest_api_enabled` filter. Classic LearnDash REST namespace defaults to `ldlms`; v2 routes use `ldlms/v2`. The namespace constant is `LEARNDASH_REST_API_NAMESPACE`. Do not hard-code every route base. LearnDash settings allow REST base overrides for courses, lessons, topics, quizzes, groups, users, and relation subroutes. Default important v2 bases: - Courses: `/wp-json/ldlms/v2/sfwd-courses` - Course steps: `/wp-json/ldlms/v2/sfwd-courses/{id}/steps` - Course users: `/wp-json/ldlms/v2/sfwd-courses/{id}/users` - Course groups: `/wp-json/ldlms/v2/sfwd-courses/{id}/groups` - Groups: `/wp-json/ldlms/v2/groups` - Group courses: `/wp-json/ldlms/v2/groups/{id}/courses` - Group users: `/wp-json/ldlms/v2/groups/{id}/users` - Users courses: `/wp-json/ldlms/v2/users/{id}/courses` - Users groups: `/wp-json/ldlms/v2/users/{id}/groups` - User course progress: `/wp-json/ldlms/v2/users/{id}/course-progress` ## Discovery workflow For `learndash/v1`, do not guess parameters. The local source declares the manifest as the mandatory discovery workflow. Use: - `GET /wp-json/learndash/v1/manifest` - Follow each endpoint `href` to fetch the exact JSON schema. - Or inspect `GET /wp-json/learndash/v1/docs/openapi`. Most `learndash/v1` endpoints are experimental and require: ```http Learndash-Experimental-Rest-Api: allow ``` The manifest and OpenAPI documentation endpoints are public and not experimental in 5.1.9. ## Course steps endpoint Creating lesson/topic/quiz posts is not enough to build a course outline. Update the course steps relationship. Endpoint: ```text GET/POST /wp-json/ldlms/v2/sfwd-courses/{course_id}/steps ``` Read permission: - Anonymous users are denied unless the filter `learndash_rest_course_steps_allow_anonymous_read` returns true. - Admin users can read. - Other logged-in users are denied by the controller. Update permission: - Requires `edit_courses`. Expected update body shape is an object keyed by LearnDash post types. Lesson, topic, and quiz IDs are object keys, not list items: ```json { "sfwd-lessons": { "123": { "sfwd-topic": { "456": { "sfwd-quiz": { "789": {} } } }, "sfwd-quiz": {} } }, "sfwd-quiz": { "999": {} } } ``` Common bug: sending `[123, 456]` arrays. The controller expects nested objects where IDs are keys. ## Course custom pagination in 5.1.7+ Course REST fields expose `lessons_per_page`, `lesson_per_page_custom`, and `topic_per_page_custom`. When custom pagination is enabled, use positive integers for both custom values. LearnDash 5.1.7+ normalizes empty or zero values to the corresponding global setting, then to `LEARNDASH_LMS_DEFAULT_WIDGET_PER_PAGE` if the global value is also invalid. Do not use zero to mean "show all" for these course-specific fields. Disable custom pagination when the course should inherit the global values. ## Enrollment and relation endpoints Course users: - `GET /ldlms/v2/sfwd-courses/{id}/users` - `POST /ldlms/v2/sfwd-courses/{id}/users` with `user_ids` - `DELETE /ldlms/v2/sfwd-courses/{id}/users` with `user_ids` The controller calls `ld_update_course_access()` for enrollment and rejects open courses with `learndash_rest_rejected_course_open`. It can skip admin users when admin auto-enroll is enabled. User courses: - `GET/POST/DELETE /ldlms/v2/users/{id}/courses` - Individual course relation routes can update enrollment dates with `enrolled_at`. Group courses: - `GET/POST/DELETE /ldlms/v2/groups/{id}/courses` with `course_ids`. - Mutations call `ld_update_course_group_access()`. Group users: - `GET/POST/DELETE /ldlms/v2/groups/{id}/users` with `user_ids`. - Mutations call `ld_update_group_access()`. User groups: - `GET/POST/DELETE /ldlms/v2/users/{id}/groups` with `group_ids`. Permission model: - Most relation reads/writes require LearnDash admin permissions. - Group leaders can read users for groups they administer. - Group leaders are not generally allowed to mutate group users through these controllers. ## Extending REST safely LearnDash's v2 REST loader registers controllers on `rest_api_init` and exposes the controller list through `learndash-rest-api-controllers`. Prefer adding a separate namespaced WP REST controller for custom app APIs unless the task specifically needs to extend LearnDash's own route catalog. For custom fields on LearnDash posts: - Register real post meta with `show_in_rest` when possible. - For LearnDash metabox settings, inspect `register_rest_fields()` in the v2 posts controller and the specific post-type controller before inventing field names. - Keep permission callbacks explicit. Do not expose course progress or enrollment writes publicly. ## Progress routes are read-only The v2 user progress routes expose course headers, ordered steps, quiz progress, and challenge-exam state through GET requests. In 5.1.9, `/users/{id}/course-progress`, its `/{course}` route, `/{course}/steps`, and `/{course}/exam` do not register a general progress-write method. Do not invent a PATCH contract from the response schema. For a custom mutation endpoint, authorize the actor against the target user and call LearnDash's completion/progress APIs; use `learndash-course-progress` for that lifecycle. ## Headless integration checklist 1. Check REST is enabled in constants and LearnDash settings. 2. Discover actual bases from settings or manifest/docs instead of assuming defaults on customized sites. 3. Authenticate with Application Password, cookie+nonce, OAuth/JWT layer, or a site-specific auth plugin. 4. Fetch course posts and then fetch `/steps` for the outline. 5. Use relation endpoints for enrollment/group changes, not raw usermeta. 6. Use progress endpoints for progress, not direct activity-table writes. 7. Respect pagination, batch limits, and per-item success/failure responses. 8. Cache read responses carefully and invalidate on post, access, group, and progress mutations. ## Common mistakes to reject - Guessing `learndash/v1` request bodies without first reading manifest `href` details. - Hard-coding route bases when LearnDash settings can change them. - Creating lessons/topics but never updating course steps. - Sending arrays to `/steps` instead of nested ID-keyed objects. - Trying to enroll users into an open course through the course-users endpoint. - Assuming group leaders can mutate all group REST relations. - Exposing a custom public route that returns locked course content without checking `sfwd_lms_has_access()`. ## Cross-references - Use `learndash-course-access` for final access checks and direct course enrollment semantics. - Use `learndash-group-access` for group membership and group-course relation behavior. - Use `learndash-woocommerce-access` for Woo-driven access changes. - Use `learndash-course-progress` for server-side completion, reset, activity, and progress-write behavior. - Use `wp-rest-api` for general WordPress REST security and schema patterns. ## References Validated against LearnDash LMS 5.1.9 local source: - `includes/rest-api/class-ld-rest-api.php` - `includes/settings/settings-sections/class-ld-settings-section-general-rest-api.php` - `includes/rest-api/v2/class-ld-rest-courses-steps-controller.php` - `includes/rest-api/v2/class-ld-rest-courses-users-controller.php` - `includes/rest-api/v2/class-ld-rest-users-courses-controller.php` - `includes/rest-api/v2/class-ld-rest-groups-courses-controller.php` - `includes/rest-api/v2/class-ld-rest-groups-users-controller.php` - `includes/rest-api/v2/class-ld-rest-users-groups-controller.php` - `includes/rest-api/v2/class-ld-rest-users-course-progress-controller.php` - `includes/settings/settings-metaboxes/class-ld-settings-metabox-course-display-content.php` - `src/Core/Modules/REST/V1/` - `src/Core/Modules/REST/Documentation_Migration/` - Official documentation: <https://developers.learndash.com/> - Verified source paths: - `wp-content/plugins/sfwd-lms/includes/rest-api/v2/class-ld-rest-posts-controller.php` - `wp-content/plugins/sfwd-lms/includes/rest-api/v2/class-ld-rest-courses-controller.php`
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.