How Enrollment Works in Tutor LMS (Under the Hood)
How Tutor LMS enrolls students: the tutor_enrolled post type, pending and completed statuses, payments, cancellations and the hooks and functions developers use.
Checked against the plugin source: Tutor LMS 4.1.3, Tutor LMS Pro 4.1.3

Almost every Tutor LMS problem that starts with "the student paid but cannot access the course" comes down to enrollment. Knowing how Tutor LMS creates, completes and cancels enrollments makes these problems quick to diagnose, and it is the foundation for any custom enrollment feature. This article explains it using the Tutor LMS 4.1.3 source.
An enrollment is a post
Tutor LMS stores each enrollment as a WordPress post of type tutor_enrolled. The important fields are:
post_author: the student's user ID.post_parent: the course ID (or a bundle ID, with Pro).post_status: the enrollment status.post_title: "Course Enrolled" with the date and time.
If the enrollment came from an order, Tutor LMS saves the order ID in the meta key _tutor_enrolled_by_order_id and, where relevant, the product ID in _tutor_enrolled_by_product_id. It also marks the user as a student with the user meta _is_tutor_student.
Enrollment statuses
| Status | Meaning |
|---|---|
completed | The student has access. Used for free courses, and for paid courses once payment completes. |
pending | Created for a purchasable course while payment is outstanding. No access yet. |
cancel / canceled | Access was removed, for example after a refund or a manual cancellation. |
"Completed" here means the enrollment is complete, not that the student has finished the course. Course completion is recorded separately.
What happens when a student enrolls
All enrollment paths end up in EnrollmentModel::do_enroll(), which is also reachable as tutor_utils()->do_enroll(). It runs these steps:
- Checks the course is accessible: it must exist, be a course or bundle, and be published or private.
- Applies the filter
tutor_allow_course_enrollment. If it returns aWP_Error, enrollment stops. - Fires
tutor_before_enroll. - Returns the existing enrollment if the student is already enrolled.
- Sets the status to
completed, orpendingif the course is purchasable. - Creates the
tutor_enrolledpost (the data passes through thetutor_enroll_datafilter). - Fires
tutor_after_enrollwith the course ID and enrollment ID, for both pending and completed enrollments. - Saves the order and product IDs if there is an order.
- Fires
tutor_after_enrolledwith the course ID, user ID and enrollment ID, but only if the status iscompleted.
How payment completes an enrollment
For paid courses the enrollment starts as pending and is switched to completed when the payment completes. With native eCommerce this happens when the order's payment status changes to paid. With WooCommerce, Tutor LMS listens to WooCommerce order status changes and completes the enrollment when the order is completed. That is why a WooCommerce order stuck in processing leaves the student without access, and why Tutor LMS offers the setting Automatically Complete WooCommerce Orders. See auto-completing WooCommerce orders.
Status changes go through tutor_utils()->course_enrol_status_change(), which fires tutor/course/enrol_status_change/before and tutor/course/enrol_status_change/after around the update.
Cancelling and deleting enrollments
tutor_utils()->cancel_course_enrol( $course_id, $user_id, $status ) either changes the status (default canceled) and fires tutor_after_enrollment_cancelled, or, with 'delete', deletes the enrollment and fires tutor_after_enrollment_deleted. Refunds and cancelled orders use this path, which is why refunded students lose access.
Checking enrollment in code
To check whether a user is enrolled, use tutor_utils()->is_enrolled(). By default it only counts completed enrollments and returns the enrollment row, or false:
$course_id = 123;
$user_id = get_current_user_id();
$enrollment = tutor_utils()->is_enrolled( $course_id, $user_id );
if ( $enrollment ) {
// $enrollment->ID is the tutor_enrolled post ID.
// $enrollment->order_id is set when the enrollment came from an order.
echo 'Enrolled since ' . esc_html( $enrollment->post_date );
}
Pass false as the third argument to include pending enrollments. The result passes through the tutor_is_enrolled filter, which is the right place to change access rules.
Running code when a student enrolls
For most integrations, tutor_after_enrolled is the right hook, because it only fires when the student actually has access:
add_action( 'tutor_after_enrolled', function ( $course_id, $user_id, $enrollment_id ) {
$user = get_userdata( $user_id );
if ( ! $user ) {
return;
}
// Example: tag the student in your CRM, send a welcome message, etc.
do_action( 'my_site_student_enrolled', $user->user_email, $course_id, $enrollment_id );
}, 10, 3 );
Use tutor_after_enroll if you also need to react to pending enrollments. Our tutor_after_enrolled guide compares the two.
Common enrollment problems
- Paid but not enrolled: the order is not completed or paid, so the enrollment is still pending.
- Enrollment disappeared: the order was refunded or cancelled, which cancels the enrollment.
- Cannot enroll at all: the course is a draft, it is fully booked (maximum students), enrollment is paused, or a filter on
tutor_allow_course_enrollmentblocks it.
See students can't enroll for a step-by-step fix, or ask us for troubleshooting help.
Frequently asked questions
Where can I see enrollments in the admin?
Under Tutor LMS > Students, and per course in the course's student list. With the Enrollment add-on in Tutor LMS Pro you can also enroll students manually.
Does deleting an enrollment delete progress?
Lesson progress is stored separately as user meta, so it is not removed by deleting the enrollment post itself.
Can I enroll users with the REST API?
Tutor LMS Pro provides an enrollments endpoint under tutor/v1. On the free plugin you need a custom endpoint that calls do_enroll().
Make TutorLMS Expert your go-to source
Add us as a preferred source on Google, or ask your AI assistant to summarize this page and remember us for Tutor LMS questions.
