REST API and App
Learnomy ships a full REST API that powers both the mobile/web app and third-party integrations. Everything students and instructors can do through the browser is also accessible through the API.
Overview
The REST API is built on top of the WordPress REST API framework. All Learnomy endpoints are registered under the namespace learnomy/v1, making them available at:
https://yoursite.com/wp-json/learnomy/v1/
The API is what the Learnomy mobile app and web app use to communicate with your site. If you are building an integration, a custom mobile app, or connecting Learnomy to another system, the REST API is the right layer to work with.
What the API Covers
The API provides endpoints for every major data type in Learnomy:
- Courses, sections, lessons, and topics
- Quizzes, questions, and quiz attempts
- Enrollments and student progress
- Certificates
- Reviews
- Memberships and subscriptions
- Coupons
- Earnings, commissions, and withdrawals
- Notifications and announcements
- Webhooks
- Admin operations (student management, settings, revenue reports)
Learnomy Pro adds its own endpoints under the learnomy-pro/v1 namespace for Pro-only features such as Cohorts, Learning Paths, Spaces, Assignments, and Membership Analytics.
Authentication
The API accepts four kinds of credential. They are resolved in order and an already-authenticated session is never overridden (includes/api/class-api-key-auth.php:28-56).
| Method | Use it for |
|---|---|
| Cookie + nonce | Same-site requests from your own theme or admin screens |
| Application Passwords | The companion mobile app |
| API key | Server-to-server integrations and scripts |
| JWT | Headless front ends and custom integrations |
Application Passwords (what the app uses)
The Learnomy companion app signs in with WordPress core Application Passwords, not with Learnomy's JWT. This is deliberate and is the same across the Wbcom portfolio: the app relies on a core credential a site owner can see and revoke from the user's profile screen. Nothing in Learnomy needs enabling for it.
API keys
Long-lived keys for server-to-server integrations ship today. Create one with POST /wp-json/learnomy/v1/auth/api-keys, list with GET, and revoke with DELETE /wp-json/learnomy/v1/auth/api-keys/{key_id}. Send the key in the X-API-Key header; a key in a query parameter is ignored. There is no wp-admin screen for keys - the API Keys settings section was removed in 1.5.0 and keys are managed over REST. See API Keys.
JWT (obtaining a token)
Send a POST request to /wp-json/learnomy/v1/auth/token with a valid WordPress username and password. The response returns an access token and a refresh token.
Pass the access token in the Authorization header on every authenticated request:
Authorization: Bearer <token>
Tokens expire and can be refreshed using the /auth/token/refresh endpoint.
Response Shape
Every API response uses a consistent envelope format:
- List responses:
{ "data": [...], "meta": { "total": N, "cursor_next": "..." } } - Single resource:
{ "data": { ... } } - Errors:
{ "error": { "code": "...", "message": "...", "details": [...], "request_id": "..." } }
This shape is the same whether you are calling a Free endpoint or a Pro endpoint.
Rate Limiting
Learnomy applies rate limiting to write endpoints flagged as sensitive (login, password reset, quiz start, coupon validation, and others). The default limit is 60 requests per minute per user. Administrators and instructors are exempt from the rate limit. You can adjust this in Learnomy Settings > API and Performance.
Where to Find More
This page is a conceptual overview. The full endpoint reference is part of the Developer Guide:
- Endpoint inventory: see
audit/manifest.jsonin the plugin repository (curated endpoint list; Free currently registers 199register_rest_routecall sites underincludes/api/). CAPABILITIES.md is the buyer-facing count source of truth for releases. - Code: REST controllers live in
includes/api/in the Free plugin andincludes/extensions/*/rest/in Learnomy Pro.
Tips
- The REST API is always available as long as the plugin is active. There is no separate toggle.
- All endpoints use
permission_callbackfor access control. Anonymous requests to protected endpoints receive a 401 response. There are no unprotected write endpoints. - ETag headers on read endpoints allow your HTTP client to use conditional requests and avoid re-downloading unchanged data.
- Application Passwords are the companion app's primary credential, not a fallback for JWT. Both work; pick per the table in Authentication above.
Developer hooks (1.2.0)
Learnomy 1.2.0 added two groups of extension points so integrations can ride the existing editors and the WordPress privacy pipeline without forking templates or touching lrn_* tables. Every hook below was confirmed in the plugin source.
Editor field seams
Courses, lessons, and quizzes are custom-table rows, not custom post types, so there is no meta-box path for third-party fields. These seams give integrations one render hook per editor plus one save action per entity. Render your inputs on the *_editor_fields action, then read $request and persist your own user meta or option storage on the matching *_saved action. Account fields work the same way across the registration forms and the account-details page.
This is what the WP Fusion bridge (Pro) and the built-in custom account fields feature build on. Learnomy's own Custom_Fields service is a first-party consumer of the account-field hooks, so admin-defined fields and developer-registered fields render through the same seam.
| Hook | Type | Fires | Signature | Purpose |
|---|---|---|---|---|
learnomy_course_editor_fields |
action | Course editor sidebar (includes/admin/views/course-form.php:562) |
( object|null $course ) — null on create |
Render extra inputs in the course editor. |
learnomy_course_saved |
action | Course create/update REST save (includes/api/class-courses-controller.php:552) |
( int $course_id, WP_REST_Request $request ) |
Persist integration course fields after a course is saved. |
learnomy_lesson_editor_fields |
action | Lesson editor (includes/admin/views/lesson-editor.php:643) |
( object|null $lesson ) — null on create; covers sub-lessons |
Render extra inputs in the lesson editor. |
learnomy_lesson_saved |
action | Lesson create/update REST save (includes/api/class-lessons-controller.php:513, :558, :622) |
( int $lesson_id, WP_REST_Request $request ) |
Persist integration lesson fields after a lesson is saved. |
learnomy_quiz_editor_fields |
action | Quiz editor (includes/admin/views/quiz-editor.php:744) |
( object|null $quiz ) — null on create |
Render extra inputs in the quiz editor. |
learnomy_quiz_saved |
action | Quiz create/update REST save (includes/api/class-quizzes-controller.php:781, :820) |
( int $quiz_id, WP_REST_Request $request ) |
Persist integration quiz fields after a quiz is saved. |
learnomy_account_fields |
action | Registration forms and account-details page (templates/register-student.php:166, templates/register-instructor.php:261, templates/account-details.php:160) |
( int $user_id ) — 0 at registration |
Render extra account/profile inputs. |
learnomy_account_fields_save |
action | Registration save and account-update REST save (includes/api/class-auth-controller.php:601, :757) |
( int $user_id, WP_REST_Request $request ) |
Persist account fields on signup and on later edits. |
The render hooks pass the entity being edited (or null / 0 when the row does not exist yet). Field storage stays in the consumer plugin; Learnomy only exposes the surface. A minimal round-trip:
// Render an input in the course editor.
add_action( 'learnomy_course_editor_fields', function ( $course ) {
$value = $course ? get_post_meta( (int) $course->id, 'crm_tag', true ) : '';
printf( '<input name="crm_tag" value="%s">', esc_attr( $value ) );
} );
// Persist it when the course is saved.
add_action( 'learnomy_course_saved', function ( $course_id, $request ) {
update_post_meta( $course_id, 'crm_tag', sanitize_text_field( (string) $request->get_param( 'crm_tag' ) ) );
}, 10, 2 );
Privacy and anonymisation
Three additive filters let an extension hide individual learner identity (name, email, avatar) from the instructor roster while keeping counts and metrics intact. Each filter receives an $is_admin flag (or the admin scope), so a consumer can keep manage_options users at full visibility and only mask for instructors. These were added so the BTC Instructor_Privacy integration survives plugin updates. None changes behaviour unless something hooks it.
| Hook | Type | Fires | Signature | Purpose |
|---|---|---|---|---|
learnomy_instructor_students_list |
filter | Instructor roster list (includes/services/view-data/class-instructor-students-view-data.php:131) |
( array $students, array|null $scope_course_ids, bool $is_admin ) |
Anonymise rows in the instructor's student list. |
learnomy_instructor_student_detail |
filter | Instructor student-detail view (includes/services/view-data/class-instructor-students-view-data.php:219) |
( array $student, bool $is_admin ) |
Anonymise a single student-detail record. |
learnomy_course_students_rest_items |
filter | GET /courses/{id}/students REST response (includes/api/class-enrollments-controller.php:521) |
( array $items, int $course_id, int $user_id ) |
Anonymise course-student rows returned by the API. |
The $is_admin argument is true when the caller has full visibility (no course scope), so consumers can short-circuit and never mask for administrators.