Course Builder API

Build and deliver online courses with ease

Base URL: https://api.wemasy.nl/api
ⓘ The token stays in this browser tab only (sessionStorage) and is sent to the WEMASY API only. Don't use production tokens on shared computers.

Courses

List courses

POST /services/courses/v1/courses/all Auth

The project's courses in their order, with module, lesson and student counts. `usage` shows how many courses count against the plan limit (drafts and published).

Get a course

POST /services/courses/v1/courses/get Auth

One course with its description, settings, linked product and the outline: modules with their lessons (without lesson bodies; fetch one with `lessons/get`).

Create or update a course

POST /services/courses/v1/courses/save Auth

Without `id` a new course is made (draft unless `status` says otherwise); with `id` only the fields sent are changed. Rules: - Drafts and published courses count against the plan's `courses` limit (archived ones do not). - The slug is unique in the project; without one it is made from the title. - A course can only be published with at least one published lesson, and a paid course needs a linked webshop product (`webshop_product_id`, a product of this project).

Delete a course

POST /services/courses/v1/courses/delete Auth

Removes the course with its modules and lessons (kept as soft deleted). Students lose access; their enrollment rows stay. To keep a course for its students, archive it instead.

Reorder courses

POST /services/courses/v1/courses/reorder Auth

`ids` in the new order; each course gets its index as position. Every id must be a course of this project.

Course statistics

POST /services/courses/v1/courses/stats Auth

Per course: `students` (enrolled with access), `completions` (finished every lesson), `revoked`, and `average_progress` (percent, over students with access). Pass `id` for one course; without it every course of the project is listed.

Where students get stuck

POST /services/courses/v1/courses/insights Auth

For course `course_id`, over its students with access: per published lesson in reading order how many completed it (`completed`, `completion_rate` in percent), the average and median seconds spent, and `stuck` (active students for whom it is the next lesson and who did nothing for `stuck_days` days); per quiz question, hardest first, how many answered it in their latest attempt, the share wrong and the wrong answer chosen most; and the stuck students (longest inactive first, at most 100), with the last mail the owner sent them. Per lesson `tutor_questions`: the questions students asked the AI tutor there this month; `tutor`: the switch, this course's questions this month and the project's AI answers this month against the plan's limit (null: none).

Create or update a module

POST /services/courses/v1/courses/modules/save Auth

Without `id` a module is added at the end of course `course_id`; with `id` only the fields sent change. `drip_days`: the module opens that many days after a student enrolled (empty or 0: open right away).

Delete a module

POST /services/courses/v1/courses/modules/delete Auth

Removes the module and its lessons. Refused when the course is published and this module holds its last published lessons.

Reorder modules

POST /services/courses/v1/courses/modules/reorder Auth

`ids` of the modules of course `course_id` in the new order.

Get a lesson

POST /services/courses/v1/courses/lessons/get Auth

One lesson with its full content, video and attachments (owner view: drafts too). A quiz lesson also has its `questions`, with the right answers.

Create or update a lesson

POST /services/courses/v1/courses/lessons/save Auth

Without `id` a lesson is added at the end of module `module_id` (draft unless `status` says otherwise); with `id` only the fields sent change, and `module_id` moves it to another module of the same course. The slug is unique in the course; without one it is made from the title. `content` is HTML (cleaned on save). `is_preview`: anyone may read it while the course is published. A published course keeps at least one published lesson. A quiz lesson (`type` quiz) takes `settings.pass_percent` (0 to 100, default 70) and `settings.max_attempts` (empty: unlimited), and is published once it has a question.

Delete a lesson

POST /services/courses/v1/courses/lessons/delete Auth

Refused when it is the last published lesson of a published course.

Reorder lessons

POST /services/courses/v1/courses/lessons/reorder Auth

`ids` of the lessons of module `module_id` in the new order.

Create or update a quiz question

POST /services/courses/v1/courses/lessons/questions/save Auth

Without `id` a question is added at the end of quiz lesson `lesson_id`; with `id` it is replaced. `type` single (options, correct [index]), multiple (options, correct [indexes]), true_false (correct [true|false]) or short_text (correct: accepted answers). `points` (default 1) and an `explanation` the student sees after submitting.

Delete a quiz question

POST /services/courses/v1/courses/lessons/questions/delete Auth

A published quiz lesson keeps at least one question (unpublish the lesson first). Attempts already submitted keep their score.

Reorder quiz questions

POST /services/courses/v1/courses/lessons/questions/reorder Auth

`ids`: every question of quiz lesson `lesson_id`, in the new order.

Quiz attempts

POST /services/courses/v1/courses/lessons/attempts Auth

The submitted attempts of one quiz lesson (`lesson_id`) or of every quiz in a course (`course_id`), newest first, with the student's name and email, score and whether it passed. `enrollment_id` narrows it to one student.

Reset a student's quiz attempts

POST /services/courses/v1/courses/lessons/attempts/reset Auth

Removes every attempt of enrollment `enrollment_id` on quiz lesson `lesson_id`, so the student starts that quiz again with all its attempts (for example after using up `max_attempts`). What the student completed stays: a passed quiz stays completed and a certificate stays valid. Returns the number of attempts removed and the student's new quiz status. A lesson that is no quiz, or not in the student's course, is not found.

List students of a course

POST /services/courses/v1/courses/students/all Auth

The enrollments of course `course_id`, newest first, with the customer and progress. Filter on `status` (active, completed, revoked) or `search` (email or name).

Enroll a student

POST /services/courses/v1/courses/students/enroll Auth

Gives a customer of this shop access to course `course_id` (source `manual`), by `customer_id` or `email`. Enrolling someone who already has access changes nothing; a revoked student gets access back with their progress. Counts against the plan's `students_per_course` limit.

Revoke a student

POST /services/courses/v1/courses/students/revoke Auth

Takes away the access of enrollment `id`. Progress is kept; enrolling the student again gives it back.

One student of a course

POST /services/courses/v1/courses/students/get Auth

Enrollment `enrollment_id` of course `course_id`: the student, the enrollment (status, progress, enrolled, completed, last activity, next lesson, last mail sent) and per module per published lesson whether it is done, when and the seconds spent; a quiz lesson has the attempts (score, passed, when, the student's note) and `weak_spots`: the questions wrong in the latest attempt with the student's answer, the right answer and the explanation (owner only).

Draft a nudge for a student

POST /services/courses/v1/courses/students/nudge/draft Auth

A short, warm mail for enrollment `enrollment_id` of course `course_id`: the student's first name, where they are, a tip from that lesson, in the student's language. Written by the AI (charged to the project's AI credits), else a plain template (`source` template). Nothing is sent: the owner edits it and sends it with students/nudge/send.

Send a nudge to a student

POST /services/courses/v1/courses/students/nudge/send Auth

Mails `subject` and `body` (plain text, the owner's edited draft) to the student of enrollment `enrollment_id` of course `course_id`, with a button to continue the course, from the shop like the drip mails, and keeps it (course_messages): the Insights tab shows when the last one went out. `source` (ai or template) says where the draft came from. Only on the owner's click.

Courses - Public

Course catalog

POST /services/courses/v1/public/courses/all Public

The published courses of project `project_id` for the website: title, slug, image, the price of the linked product (paid courses), the number of lessons and the preview lessons anyone may open. No lesson content.

Course page

POST /services/courses/v1/public/courses/get Public

One published course of project `project_id` by `id` or `slug`, for its page on the website: description, price of the linked product, and the outline (modules and lesson titles; only preview lessons are `accessible`). No lesson content.

Preview lesson

POST /services/courses/v1/public/courses/lesson Public

The content of a free preview lesson of a published course of project `project_id`, for the course page on the website. Any other lesson answers 403 `not_enrolled`: students open those through the customer API.

Courses - Website

Verify a certificate

POST /services/courses/v1/public/courses/certificate/verify Public

Checks a verification code of this shop's certificates. Answers only the student's name, the course title and the completion date; 404 `certificate_not_found` otherwise.

Customer Dashboard - Courses

My courses

POST /services/courses/v1/customer/courses/all Auth

The courses the signed-in customer is enrolled in (with access), newest enrollment first, with progress, the number of lessons and the next lesson to open.

Course outline

POST /services/courses/v1/customer/courses/get Auth

A course by `id` or `slug` with the customer's enrollment (null when not enrolled) and the outline: per lesson `accessible`, `completed`, and when it cannot be opened the `reason` (`not_enrolled`, `locked`) and `unlock_at`. `can_enroll` is true for a published free course the customer is not enrolled in; a paid course shows its `product` to buy. `tutor` is true for a student with access when the course has the AI tutor on.

Open a lesson

POST /services/courses/v1/customer/courses/lesson Auth

The lesson's content when the customer may read it: enrolled and the module's drip date passed, or a preview lesson. Otherwise 403 with `reason` `not_enrolled`, or `locked` with `unlock_at`. Also gives the previous and next lesson ids in reading order.

Complete a lesson

POST /services/courses/v1/customer/courses/complete Auth

Marks a lesson done for the signed-in student and answers the updated enrollment (`progress_percent`; `status` becomes `completed` when every published lesson is done). Needs an enrollment and an open lesson: 403 `not_enrolled` or `locked` otherwise. `seconds_spent` (optional) adds to the time on the lesson. A quiz lesson cannot be marked complete (422 `quiz_must_be_passed`): passing its quiz completes it.

Enroll in a free course

POST /services/courses/v1/customer/courses/enroll Auth

Enrolls the signed-in customer in published free course `course_id`. Already enrolled: the same enrollment comes back. A paid course answers 403 `paid_course` with its `product_id` (buy it to get access), access the owner revoked 403 `enrollment_revoked`, and a full course 422 `students_limit_reached`.

Get a quiz

POST /services/courses/v1/customer/courses/quiz Auth

The questions of a quiz lesson (type, question, options, points; never the right answers or explanations), the pass percentage, the attempts allowed and used, and whether the student passed. Same access as opening the lesson: 403 `not_enrolled` or `locked`.

Submit a quiz

POST /services/courses/v1/customer/courses/quiz/submit Auth

`answers` keyed by question id: an option index (single), a list of indexes (multiple), true or false (true_false) or text (short_text). The server scores it and answers the score, `passed`, and per question whether it was right with its explanation (never the right answer itself). Passing completes the lesson. `feedback` is the student's personal note (text, source ai or template, the lesson to reread), or null when the course switched ai_feedback off; while attempts are left it never gives an answer away. 403 `quiz_no_attempts_left` when the attempts are used up; 403 `not_enrolled` or `locked` as for completing a lesson.

Download my certificate

POST /services/courses/v1/customer/courses/certificate Auth

The signed-in student's certificate of course `course_id` as a PDF (`format` pdf, the default), or its details and verification code (`format` json). 404 `certificate_not_found` when the course is not completed or gives no certificate.

Ask the course tutor

POST /services/courses/v1/customer/courses/tutor/ask Auth

A question (`message`, at most 1,000 characters) about lesson `lesson_id` (with `course_id` or `slug` when given: it must be that lesson's course), with the last messages of the conversation as `history` ([{role: user|assistant, content}], the client keeps them; the newest 6 are used). Answers `reply`, `source` (ai, or fallback: the tutor is not available right now) and `replaced` (true when the AI's text gave a quiz answer away and a hint to reread a lesson was given instead). Only for a student enrolled with access whose module is open (a preview is not enough): 403 `not_enrolled` or `locked`; 403 `tutor_off` when the course has no tutor; 429 `tutor_month_limit` or `tutor_daily_limit` when the plan's month or the student's day is used up. Another shop's lesson is 404. The question and the reply are not kept.