Skip to main content
This page covers all backend functions for courses and shows how to use them in .ef page templates. See Courses for setup and enrollment.

Backend functions

All functions are available in <script scope="backend"> blocks and in .ef files.
Who gets full lessons. Every function answers for the signed-in customer. A student with active access to a course gets its lessons in full. Anyone else gets the outline: titles, ids, type and order, with locked: true on each lesson. A draft course is hidden from listings and from visitors who are not enrolled in it (an enrolled student and a preview still see it). See Courses on your own pages.

getCourses(opts?)

Returns all courses for the brand, each with is_assigned and started for the current customer.
sort supports:
  • enrollment_oldest_unfinished_first (default)
  • title_asc
  • title_desc
  • preferred (when used, pass preferredOrder: ['slug-or-title', ...])

getCategoriesWithCourses(opts?)

Same as getCourses but grouped by category. Returns an array of { category_slug, category_name, courses[] }.

getCoursesByCategorySlug(opts)

Returns courses in a single category. Useful for “Daily practices” or “Advanced” sections on a dashboard.

getCoursesForCustomer(opts?)

Returns only courses the current customer is enrolled in (same as getCourses({ assignedOnly: true })). Returns [] if no session or no enrollments.

getCourseForCustomer(opts)

Returns a single course with enrollment check — is_assigned and started are set for the current customer. Also returns the full modules[] and contents[] tree, plus progress_percent and completed_content_ids. Use this on the course detail page.
Returns null if no course with that slug exists.

getCourseBySlug(slug)

Returns a single course by slug. Use it for course metadata and the outline, for example on a sales or teaser page. A visitor without access gets the outline (lessons with locked: true); a student with access gets the full lessons. A draft course returns null for anyone not enrolled in it.

getCourseLessons(opts)

Returns every lesson of a course in order (module order, then lesson order) as one flat list. Useful for an app-style lesson list without the course portal.
A student with active access also gets each lesson’s content, files and takeaways; everyone else gets the outline. A draft course returns [] unless the student is enrolled in it.

getCourseLesson(opts)

Returns the resolved current lesson payload for lesson-player pages, including currentLesson, prevLessonUrl, nextLessonUrl, and progress stats.
Leave out an argument (or pass an empty value) and it is read from the page’s URL: on a page with the slug course/{course}/module/{module}/lesson/{lesson}, getCourseLesson() finds the lesson the visitor opened. In a template, the URL segments are also on request.route_params (request.route_params.course, .module, .lesson).

getOrderCourses()

Returns the courses the visitor’s latest order in this session gives access to, as a list of { title, url }. Use it on your own thank-you page to send a course buyer straight to the course. It returns [] when the session has no order or the order unlocks no course.
The link opens the course page: your course domain and path when you set one in the course settings, otherwise /course/{slug} on the domain the buyer is on. A buyer who is not signed in yet is asked to sign in first. When a project has no thank-you page, the built-in one shows the same links.

getCompletedLessonsCount(opts?)

Returns the total number of completed lessons across all enrolled courses, or for a specific course when a slug is provided.
Returns 0 if the customer has no enrollments or no completed lessons.

getCourseProgress(opts?)

Returns a structured progress summary with completed/total lesson counts per course and an overall total. Useful for dashboards.

request.route_params

For dynamic slug pages (e.g. guided-course/{course}.ef), the slug is available via request.route_params.course.

Course list page

Show all courses, sorted so enrolled courses appear first. Use is_assigned and started to drive the CTA label.

With category tabs


Course detail page

Resolve the slug from the URL, check enrollment with getCourseForCustomer, and gate content.

Auth wrapper for course pages

Use the auth wrapper pattern (@extends) so the login check and common data load happen once across all course pages:
Then each course page:

Checking access to a specific product

If you want to gate course access by checking whether the customer bought a specific product (rather than relying on enrollment), you can cross-reference with getOrders():
Prefer the enrollment-based check (getCourseForCustomer) — it is more reliable and does not require iterating order history. Use the order-history fallback only if enrollment is not being set up via product linking.

Lesson types and content

Each lesson in mod.contents has a type field: Additional fields on each lesson:

Progress API

All progress routes are prefixed with /api/course/courses/:courseId/. Authentication is via the active course session (cookie). courseId, moduleId, and contentId are numeric IDs.

Mark a lesson complete

Returns { success: true, message, stats: { completed_lessons, total_lessons, completion_percent } }.

Save general progress

courseId, moduleId, and contentId go in the URL for the lesson-specific routes. The general saveProgress route keeps them in the body — useful when you want to upsert a progress record with a specific completed value (e.g. un-completing).

Save video position

Get all progress for a course

Get progress for a single lesson

Get course stats

The page receives the initial completed IDs via course.completed_content_ids. Use this array to mark lessons visually on load, then update the UI optimistically when the API call succeeds.