Course Access and Enrollment
Enrollment is the record that a student has access to a course. Learnomy grants that access based on the course's access type (free, paid, membership, or both) and keeps one enrollment row per student, per course, per source, so a student who reaches a course through more than one path never loses access when one path ends.
What you can do
- Let a signed-in student enrol in a free course with one click and no payment.
- Route paid and members-only courses to checkout or the membership page instead of a plain Enroll button, so nobody reaches a course without the access they paid for.
- Grant access by hand from the admin, either one student at a time or in bulk from a list of emails.
- Remove a student's access to a single course, or clear all of a student's enrollments at once.
- Require students to finish prerequisite courses before they can enrol.
- Keep membership access and a one-time purchase side by side on the same course, because each source is its own enrollment row.
- Have fixed-term (time-limited) enrollments expire automatically once their access window ends.

How to use it
Step 1 -- set how the course is reached
The Enroll button a student sees is driven by the course's access type. Edit the course, open the Pricing & Access section, and pick a pricing model. Free courses show an instant Enroll button; paid and members-only courses show a Buy or membership CTA instead. See Pricing and Access Models for the full model list.
Step 2 -- let students enrol themselves
On the single course page (/courses/<slug>/) a student who is not yet enrolled sees the enroll card in the sidebar:
- Free course -- an Enroll Now button that enrols them instantly and drops them into the course.
- Paid course -- a Buy button that routes to checkout; the enrollment is created only after payment completes.
- Members-only course -- a link to the membership page; the enrollment is created from their active membership.
You can also drop an enroll button anywhere on the site with the shortcode [learnomy_enrollment_button course_id="123" text="Enroll Now"]. It renders the right CTA for the course and shows an "Already enrolled" state when the current user already has access.
Step 3 -- grant access by hand (admin)
When you need to enrol someone yourself, open the course's overview page in wp-admin and use the Enroll a student control. Enter the student's email or username (there is no dropdown of every user, so it stays fast on large sites). This creates a manual-source enrollment. You can also enrol a student into a course from that student's detail page.
Step 4 -- enrol a list in bulk
To onboard a group, use the course's bulk-enroll control and paste or upload a list of emails. Unknown emails are provisioned as new accounts with a set-password invite. The list is processed in chunks and capped per request so a large paste never times out. See Bulk Enroll from a List.
Step 5 -- remove access
To revoke access, remove the student's enrollment from the Students screen. You can remove a single course enrollment, or clear every enrollment a student holds in one action.
Settings & options
- Access type decides the enroll path per course (
free/paid/membership/paid_or_membership). The public enroll action never trusts a source sent by the browser; the server derives it from the course's access type plus the student's verified state (a completed transaction or an active membership). - Prerequisites -- when a course requires prerequisite courses, enrollment is refused until the student has completed them. Configure these in the course editor; see Course Prerequisites.
- Enrollment sources -- each enrollment carries a source:
free,purchase,membership,subscription,manual, orwebhook. The table is unique on(user_id, course_id, source), so a student can hold both a membership enrollment and a purchase enrollment on the same course without one overwriting the other. - Enrollment status --
active,completed,cancelled,expired, orpending. Fixed-term enrollments are moved toexpiredautomatically when their access window closes. - External enrollment -- courses set to the closed model are enrolled from an outside system through the incoming webhook. See Incoming Enrollment Webhook.
For developers
Service (Learnomy\Services\Enrollment_Service):
enroll( int $user_id, int $course_id, string $source = 'free', array $options = [] )-- the single write path; checks prerequisites, dedupes on source, returns the enrollment id or aWP_Error. Adapters call this directly with their own verified source.unenroll( int $enrollment_id )-- removes one enrollment.can_access( int $user_id, int $course_id )-- delegates toPermission_Engine::can( $user_id, 'access_course', $course_id ).retake( int $user_id, int $course_id )-- reopens a completed course when the per-courseallow_retakesetting is on.
Permissions and capabilities:
- Access decisions run through
Permission_Engine::can( $user_id, 'access_course', $course_id )-- never a direct capability check. - The
learnomy_can_access_coursefilter (result, user_id, course_id) lets add-ons override the access decision. - Self-enrollment requires the
learnomy_enrollcapability.
Hooks fired:
learnomy_student_enrolled-- ( enrollment_id, user_id, course_id, source ). Fires on every enrollment; notifications, lesson-unlock scheduling, and the outgoing webhook listen here.learnomy_enrollment_expired-- ( enrollment_id, user_id, course_id ). Fired by cron when a fixed-term enrollment lapses.learnomy_course_completed-- ( enrollment_id, user_id, course_id ).learnomy_course_retaken-- fired when a completed course is reopened.
REST (learnomy/v1):
POST /courses/{course_id}/enroll-- self-enroll for the logged-in user.sourceandplan_idare deliberately not accepted; the server derives the source. Paid or members-only courses without proof return402with acheckout_urlormembership_url.DELETE /enrollments/{id}-- unenroll a single row.DELETE /students/{user_id}/enrollments-- remove every enrollment for a user (capmanage_settings).POST /admin/courses/{id}/enroll-- admin enrol by email/username (manual source).POST /admin/courses/{id}/bulk-enroll-- admin CSV/paste bulk enroll, capped bylearnomy_bulk_enroll_max.POST /admin/students/{id}/enroll-- admin enrol a student into a course from the student detail page.