# Courses & Content


A course is the thing you deliver: what a buyer gets after the checkout in the Selling
chapter, what an invite hands out, what a student opens on your site. This chapter is
about building it — the tree of modules and lessons, the text and video inside them, the
checklists that keep students moving — and about the few settings that decide how
students move through it.

On Omumu you build a course by describing it. Say Mira has a webinar's worth of notes on
automating invoice reminders: she tells her assistant what the course is and who it is
for, and the assistant creates the course, its modules, its lessons and their content
through the course tools, asking her before each step it is unsure about. Everything it
makes is an ordinary course she can open and edit in the admin. The rest of the chapter
shows the conversation and, for each step, the pages in the admin that do the same. A
path such as *Products → Courses* names a group in the admin's navigation — the rail on
the left on a laptop, behind the Menu button on a phone — and the page under it.

One rule to know before you start: **there is no draft step.** A course, a module, a
lesson or a content block is live for students with access the moment it is saved. Build
before you sell, or build on a copy.

## Concepts

**The course tree.** A course holds modules in order; a module holds lessons in order; a
lesson holds content blocks in order. Each level has a title, a description and an
optional image. The description is what a student reads on the course's home page or the
module list; the blocks are the lesson itself — the "meat". A content block is rich text
(you can write it in Markdown) with, optionally, one attached file: a video, a PDF, an
audio recording, an image.

**Resources.** Every uploaded file on your site is a resource in one site-wide library:
course videos, lesson PDFs, podcast audio, generated images, transcripts. A block attaches
a resource; a resource can be attached in several places, and it cannot be deleted while
anything still uses it. Videos stream from the content delivery network, not from your
site, so a lesson with video plays the same for one student and for a thousand.

**Checklists and completion.** A lesson can carry a checklist: short, self-attested tasks
("Log every repeated task for five working days"), each worth a few points of XP. Ticking
them is how a student tells the course they did the work. Completion is derived from
content, never configured: a lesson is complete when the student has opened it, ticked
every checklist item, watched every video to the course's watch threshold, and handed in
its assignment if it has one; a module when all its lessons are complete; a course when
all its modules are. Adding content later adds requirements for students who are not yet
done; it never un-completes anyone.

**Gating.** A course is either *open* — course access opens every module — or
*sequential*, where completing one module unlocks the next. That is the whole mechanism:
set the mode, and the unlocking is built in. The *watch threshold* says how much of a
video counts as watched (a quarter, half, three quarters, 90 % or all of it; three quarters
by default). There is no drip by date: Omumu unlocks on what a student has done, not on
the calendar.

**Access.** A student gets into a course by holding access to it — bought through an
offer (the Selling chapter), accepted through an invite, or granted by you — and signs in
to open it. A course can instead be switched to *open access*, which lets anyone view it
without signing in. You, as the site's Headmaster, can open every course you built.

**Assignments.** An assignment is a piece of work you expect back, attached to exactly
one scope — a course, a module or a lesson — with the rating criteria the AI rater and
your staff read when they judge a hand-in. Attaching one to a lesson puts the hand-in form
on that lesson's page and makes handing in part of completing it. This chapter shows how
to create one; the Teaching & Feedback chapter is about what happens when work comes in.

**Quizzes.** A quiz is a set of questions (single or multiple choice), each answer worth
points towards one or more *buckets* — the result categories. Whoever takes the quiz lands
in the bucket with the highest score, and a bucket can act: tag the person, start an
email sequence, grant a course, redirect to a page, show a message. A quiz can ask for an
email before it starts or after it ends, so it works on visitors as well as students. It
is a tool for finding out who someone is as much as what they know; the Audience &
Email chapter picks up the audience side.

## Hands-on

### Build a course by talking to your assistant

Mira says: *"Create a course called Invoice Reminders on Autopilot — for solo
consultants who chase late payments by hand. Three modules: find the pattern, build the
reminder, hand it off. Two lessons each. I'll give you the content."*

The assistant creates the course with its title and tagline, then each module and each
lesson in order, and reports the course's address on your site. If you have the
`create-course` skill (the Product Skills chapter), the assistant will interview you
first and propose the structure before it builds anything; without it, it builds what
you asked for. Then Mira pastes her notes lesson by lesson — *"Lesson 1.1: here are my
notes, make them the lesson"* — and the assistant writes each one as a content block.

Underneath: `omumu_course_create` makes the course (title, tagline) and returns its
public address; `omumu_module_create` and `omumu_lesson_create` build the tree;
`omumu_lesson_resource_create` writes a block, in Markdown, into a lesson. A lesson's
description, a module's and the course's take Markdown too. `omumu_course_update` sets
the description, the gating mode and the watch threshold; `omumu_course_get`,
`omumu_module_list` and `omumu_lesson_resource_list` read the tree back.

In the admin: *Products → Courses → New Course* asks for a title, a description and an
image and lands you on the course overview — the modules, assignments and students
cards, the open-access and gating switches, the watch threshold. *Modules → New Module*
lands on the module list; a module's page lists its lessons; a lesson's page has *Edit*
(title, description, image), *Resources* (its content blocks: a title, the text, and an
optional file) and *Checklist*. *View lesson* opens the lesson as a student sees it.

Two things to know about what comes back. Reads return the content in the editor's own
JSON, not the Markdown you wrote, so an assistant asked to *edit* an existing block has
to work from that. And a module or lesson your assistant creates cannot be deleted or
reordered by it — those are admin-UI actions today. Build in the order you want to keep.

### Give students something to tick

Mira says: *"Add a checklist to lesson 1.1: log every invoice you sent this month, mark
the ones paid late, note how many reminders each took. Ten XP each."*

`omumu_checklist_batch_create` writes the whole list in one call (up to a hundred
items); `omumu_checklist_item_create`, `_update`, `_reorder` and `_delete` adjust it. In
the admin, a lesson's *Checklist* page adds items one line at a time and drags them into
order.

### Add a video

Mira records a screencast of the reminder spreadsheet and says: *"Here's the video for
lesson 2.1."*

A video never travels through the conversation. The assistant asks for an upload slot
with `omumu_resource_upload_video_presign`, the file goes straight to storage, and the
assistant attaches the resulting resource to a content block (`omumu_lesson_resource_create`
or `_update` with the resource's id). Any other file — a PDF, an audio recording, a
document — goes the same way with `omumu_resource_upload_presign`. `omumu_resource_list`
and `omumu_resource_get` find what is already uploaded, with each video's processing
status; `omumu_resource_update` renames or describes a file.

In the admin, the site's Resources page (it has no entry in the navigation yet; open
`/admin/resources`) takes drag-and-drop uploads and shows, per file, where it is used; a
content block's form uploads a file directly into the block. A video counts as watched at
the course's threshold, which feeds completion.

On a plan with transcription, attaching a video or audio file to a lesson queues a
transcript automatically; the transcript can be saved as a text resource of its own, and
it is part of what the AI rater reads when it judges hand-ins against that lesson.

### Give it a picture

Mira says: *"Make a cover image for the course: a tidy desk, one envelope, morning
light."*

`omumu_generate_image` makes an image from a prompt and, if you name a course, module or
lesson, attaches it at once (landscape by default — the shape course cards use);
`omumu_set_image` attaches an image resource that already exists. In the admin, the image
field on the course, module and lesson forms takes a dropped file or generates one from a
prompt in place. Image generation is a plan feature, and a higher plan uses a stronger
model.

### Unlock modules in order

Mira says: *"Make the course sequential, and count a video as watched at 90 %."*

`omumu_course_update` with the gating mode and the watch threshold; in the admin, the
switches on the course overview. Under sequential gating a student sees every module in
the navigation but can open only the ones they have reached, and completing a module —
all its lessons, each with its checklist, videos and assignment — opens the next.

### Copy what works

Mira's second course reuses the time-audit module from her first. *"Copy Week 1 from
Automate Your Week into the new course."*

`omumu_module_copy` moves a copy of a module, lessons and blocks included, into another
course; `omumu_course_duplicate` copies a whole course. In the admin, every course,
module and lesson has *Duplicate*, and modules and lessons have *Copy into…* for another
course or module. A duplicated course keeps the same title, gating mode and watch
threshold, so rename it.

### Ask for work back

Mira says: *"Add an assignment to lesson 3.2: hand in a screenshot of your first automatic
reminder. Rate it on whether the reminder names the invoice, the amount and the date."*

`omumu_assignment_create` attaches an assignment to exactly one of a course, module or
lesson, with its rating criteria, as a draft; `omumu_assignment_update` publishes it and
can move it to another scope in the same course; `omumu_assignment_list` shows them. In
the admin, the course overview's *Assignments* card lists them with *New Assignment*.
What a student sees, how the AI rates a hand-in and how you reply is the Teaching &
Feedback chapter.

### Build a scored quiz

Mira wants to know, before someone buys, how much of their invoicing is already
automated. *"Make a three-question quiz that sorts people into 'By hand', 'Half way' and
'Automated', and tag them with the result."*

Six calls build it: `omumu_quiz_create` (a label), `omumu_question_create`
per question (single- or multiple-choice), `omumu_answer_create` per answer,
`omumu_bucket_create` per result with what it should do (a tag, a sequence, a course, a
redirect, a message), and `omumu_option_point_batch` to give each answer its weight
towards each bucket. `omumu_quiz_get` returns the whole scoring matrix;
`omumu_quiz_update` links an opt-in form before or after the quiz. In the admin,
*Sales and marketing → Quizzes* has the same editor and a results page per quiz, and the quiz's page
shows the public address to share. What a quiz does for your audience — the buckets'
sequences, tags and courses — is the Audience & Email chapter.

### Look at it as a student

*View lesson* on any lesson page, or the course's own address, opens the student side:
the course home with its modules and any hand-in forms, then the lessons with their
blocks, checklist and navigation. You can open any course you built in the admin. A
course your assistant built is a different matter: the agent's create does not give you
student access, so to walk it as a student, grant yourself access first (*Customers →
Users*, your own user, *Manage Course Access*).

### What you cannot do yet

Stated plainly: modules and lessons have no draft state, so there is no way to stage a
week before students see it; reads over the connection come back in the editor's JSON
rather than Markdown; your assistant can create modules and lessons but not delete or
reorder them; and unlocking is by completion only, never by date. A new site may also
list one or two of Omumu's own onboarding courses on its student side — they are
read-only copies for you, not for your students.

## Reference

### `omumu_answer_create`

Creates an answer option for a question. To enable scoring, link this answer to one or more buckets using omumu_option_point_create after creating the answer.

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `label` | string | yes | The answer label shown to users |
| `questionId` | string | yes | The question ID |
| `value` | string | no | The answer value (defaults to label) |

### `omumu_answer_update`

Updates an answer option's label or value

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `answerId` | string | yes | The answer ID |
| `label` | string | no | New answer label |
| `value` | string | no | New answer value |

### `omumu_assignment_create`

Creates an assignment attached to exactly one scope: a course, a module, or a lesson. Provide exactly one of courseId, moduleId, or lessonId — omitting all or providing more than one is rejected. COURSE scope: the assignment appears on the course homepage and is visible across all its modules and lessons. MODULE scope: the assignment is shown within a specific module and reflects that module's work. LESSON scope: the assignment is tied to a single lesson and reflects its content. New assignments default to DRAFT status; pass status=PUBLISHED to make the assignment immediately visible to students. Requires the MCP_COURSES feature (Starter tier or above).

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `asksForLight` | boolean | no | Check-in: every hand-in must include the student's own Light — green, yellow or red. Defaults to false. |
| `courseId` | string | no | Attach the assignment to this course (COURSE scope). Must not be combined with moduleId or lessonId. |
| `description` | string | no | The assignment instructions or description (Markdown) |
| `lessonId` | string | no | Attach the assignment to this lesson (LESSON scope). The owning course is derived automatically. Must not be combined with courseId or moduleId. |
| `moduleId` | string | no | Attach the assignment to this module (MODULE scope). The owning course is derived automatically. Must not be combined with courseId or lessonId. |
| `notifyStaffOnHandIn` | boolean | no | Email every Headmaster and Staff member on the site when a student hands in. Defaults to true. |
| `ratingPrompt` | string | no | Prompt shown to instructors when rating a submission (optional) |
| `status` | string | no | Initial status: DRAFT (default, not visible to students) or PUBLISHED (visible) |
| `title` | string | yes | The assignment title |

### `omumu_assignment_list`

Returns all assignments (DRAFT and PUBLISHED) for a course, with each assignment's scope kind (COURSE, MODULE, or LESSON) and the scope's id stated explicitly. Requires the MCP_COURSES feature (Starter tier or above).

- **Write category:** read-only
- **Access:** requires the `COURSE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `courseId` | string | yes | The course whose assignments to list |

### `omumu_assignment_update`

Updates an existing assignment's title, description, ratingPrompt, status, and/or scope. Provide exactly one of courseId, moduleId, or lessonId to set the new scope. The new scope must belong to the same course as the assignment. Fields not provided are left unchanged. Requires the MCP_COURSES feature (Starter tier or above).

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `asksForLight` | boolean | no | Check-in: every hand-in must include the student's own Light — green, yellow or red. Omit to leave unchanged. |
| `assignmentId` | string | yes | The assignment ID to update |
| `courseId` | string | no | New COURSE scope. Must not be combined with moduleId or lessonId. |
| `description` | string | no | New assignment instructions (Markdown) |
| `lessonId` | string | no | New LESSON scope. Must not be combined with courseId or moduleId. |
| `moduleId` | string | no | New MODULE scope. Must not be combined with courseId or lessonId. |
| `notifyStaffOnHandIn` | boolean | no | Email every Headmaster and Staff member on the site when a student hands in. Omit to leave unchanged. |
| `ratingPrompt` | string | no | New rating prompt for instructors |
| `status` | string | no | New status: DRAFT (hidden from students) or PUBLISHED (visible) |
| `title` | string | no | New assignment title |

### `omumu_bucket_create`

Creates a result bucket (outcome category) for the quiz. Users are sorted into the bucket with the highest score based on their answers. After creating buckets, link answers to them using omumu_option_point_create. Each bucket can trigger actions: send email sequence, grant course access, redirect to URL.

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | no | Bucket description |
| `name` | string | yes | The bucket name (e.g., 'Beginner', 'Advanced') |
| `quizId` | string | yes | The quiz ID |

### `omumu_bucket_update`

Updates a bucket's properties including actions (email sequence, course, redirect)

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `bucketId` | string | yes | The bucket ID |
| `courseId` | string | no | Course to grant access to |
| `description` | string | no | New description |
| `emailSequenceId` | string | no | Email sequence to subscribe user to |
| `name` | string | no | New bucket name |
| `redirectUrl` | string | no | URL to redirect to after quiz |
| `successMessage` | string | no | Message shown after quiz completion |
| `tagName` | string | no | Tag to apply to user |

### `omumu_checklist_batch_create`

Creates multiple checklist items for a lesson in one call (max 100). Atomic — all items are created or none. Useful for AI workflows that generate complete checklists at once.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `items` | array | yes | Array of objects with 'description' (required, max 1000 chars) and 'xp' (optional, 0-10000, default 0) |
| `lessonId` | string | yes | The lesson ID |

### `omumu_checklist_item_create`

Adds a new checklist item to a lesson. Checklist items are actionable tasks that students complete to earn XP. Items are auto-appended at the end of the list.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | yes | The checklist item text, max 1000 chars (what the student should do) |
| `lessonId` | string | yes | The lesson ID to add the checklist item to |
| `xp` | integer | no | XP points awarded when completed, 0-10000 (default 0) |

### `omumu_checklist_item_delete`

Permanently deletes a checklist item and any associated user progress. Remaining items are re-sorted automatically.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `checklistItemId` | string | yes | The checklist item ID to delete |

### `omumu_checklist_item_list`

Lists all checklist items for a specific lesson, ordered by sort order.

- **Write category:** read-only
- **Access:** requires the `COURSE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `lessonId` | string | yes | The lesson ID |

### `omumu_checklist_item_reorder`

Changes the sort order of a checklist item within its lesson.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `checklistItemId` | string | yes | The checklist item ID |
| `newSortOrder` | integer | yes | The new sort order position (0-based) |

### `omumu_checklist_item_update`

Updates the description and/or XP value of an existing checklist item.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `checklistItemId` | string | yes | The checklist item ID |
| `description` | string | no | New description text, max 1000 chars (optional) |
| `xp` | integer | no | New XP value, 0-10000 (optional) |

### `omumu_course_create`

Creates a new course in the user's site with the specified title and tagline. The course is live immediately for learners with access — there is no draft step — and the result's publicUrl is the address of its learner-facing course page, which requires sign-in and access to the course. After creating, add modules (omumu_module_create) and lessons (omumu_lesson_create) to build the course structure. To grant access via an opt-in form, pass the course ID to omumu_optinform_update(courseId).

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tagline` | string | no | The course tagline |
| `title` | string | yes | The course title |

### `omumu_course_delete`

Permanently deletes a course and all its content

- **Write category:** standard write
- **Access:** requires the `COURSE_DELETE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `courseId` | string | yes | The course ID to delete |

### `omumu_course_duplicate`

Creates a complete copy of an existing course including all modules and lessons

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `courseId` | string | yes | The course ID to duplicate |

### `omumu_course_get`

Retrieves detailed information about a specific course

- **Write category:** read-only
- **Access:** requires the `COURSE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `courseId` | string | yes | The course ID |
| `includeModules` | boolean | no | Whether to include modules and lessons |

### `omumu_course_list`

Lists all courses in the user's site with optional module inclusion

- **Write category:** read-only
- **Access:** requires the `COURSE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `includeModules` | boolean | no | Whether to include modules in the response |

### `omumu_course_update`

Updates the basic information of a course. The change is live immediately for learners with access — there is no draft step — and the result's publicUrl is the address of its learner-facing course page, which requires sign-in and access to the course.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `courseId` | string | yes | The course ID |
| `description` | string | no | New course description |
| `gatingMode` | string | no | Course gating mode: OPEN (default — course access opens every module) or SEQUENTIAL (completing a module unlocks the next; the unlocking is built in) |
| `tagline` | string | no | New course tagline |
| `title` | string | no | New course title |
| `watchThresholdPercent` | integer | no | How much of a video counts as watched, in percent. One of 25, 50, 75, 90, 100 (default 75) |

### `omumu_generate_image`

Generates an image from a text prompt using AI and stores it as a resource. Optionally attach the generated image directly to a course, module, or lesson. When targeting an entity, aspect ratio defaults to 16:9 (ideal for course illustrations).

- **Write category:** standard write
- **Access:** requires the `MEDIA_GENERATE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `aspectRatio` | string | no | Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4. Default: 16:9 when targeting, 1:1 otherwise. |
| `highQuality` | boolean | no | Use high quality model (slower, requires Premium/Enterprise tier). Default: false |
| `prompt` | string | yes | Text description of the image to generate. Be descriptive for best results. |
| `targetId` | string | no | ID of the entity to attach the image to. Required if targetType is provided. |
| `targetType` | string | no | Entity type to attach image to: 'course', 'module', or 'lesson'. If provided, targetId is also required. |

### `omumu_lesson_create`

Creates a new lesson within a module. Returns a compact summary {id, title, sortOrder} only — the submitted description is not echoed back, to keep responses small when authoring long-form content.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | no | The lesson description |
| `moduleId` | string | yes | The module ID |
| `title` | string | yes | The lesson title |

### `omumu_lesson_resource_create`

Creates a new content block within a lesson. Content can be Markdown or QuillDelta JSON (auto-detected). Optionally attaches an uploaded SiteResource (e.g. a video) via resourceId. Use this to add rich text content to lessons - the 'meat' of course content.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `content` | string | yes | The content in Markdown or QuillDelta JSON format. Markdown supports: # headers, **bold**, *italic*, [links](url), - bullets, 1. numbered lists, > blockquotes, `code` |
| `lessonId` | string | yes | The lesson ID to add content to |
| `resourceId` | string | no | Optional SiteResource ID (e.g. an uploaded video) to attach to this lesson part. Must belong to the same site as the lesson. |
| `title` | string | yes | Title of the content block |

### `omumu_lesson_resource_delete`

Permanently deletes a content block from a lesson.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `lessonResourceId` | string | yes | The lesson resource ID to delete |

### `omumu_lesson_resource_list`

Lists all content blocks for a specific lesson.

- **Write category:** read-only
- **Access:** requires the `COURSE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `lessonId` | string | yes | The lesson ID |

### `omumu_lesson_resource_update`

Updates an existing content block. Content can be Markdown or QuillDelta JSON (auto-detected). Optionally replaces the attached SiteResource. Pass an empty string for resourceId to clear the attachment.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `content` | string | no | New content in Markdown or QuillDelta JSON (optional) |
| `lessonResourceId` | string | yes | The lesson resource ID |
| `resourceId` | string | no | New SiteResource ID to attach (optional). Pass empty string to clear the current attachment. Must belong to the same site as the lesson. |
| `title` | string | no | New title (optional) |

### `omumu_lesson_update`

Updates a lesson's title and/or description. Description can be Markdown or QuillDelta JSON (auto-detected). Returns a compact summary {id, title, sortOrder} only — the submitted description is not echoed back, to keep responses small when authoring long-form content.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | no | New lesson description (Markdown or QuillDelta JSON) |
| `lessonId` | string | yes | The lesson ID |
| `title` | string | no | New lesson title |

### `omumu_module_copy`

Copies a module (including all its lessons and resources) from its current course into a different target course. Both the source module's course and the target course must belong to the user's site.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `moduleId` | string | yes | The ID of the module to copy |
| `targetCourseId` | string | yes | The ID of the course to copy the module into |

### `omumu_module_create`

Creates a new module within a course

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `courseId` | string | yes | The course ID |
| `description` | string | no | The module description |
| `title` | string | yes | The module title |

### `omumu_module_list`

Lists all modules in a specific course with optional lesson inclusion

- **Write category:** read-only
- **Access:** requires the `COURSE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `courseId` | string | yes | The course ID |
| `includeLessons` | boolean | no | Whether to include lessons in each module |

### `omumu_module_update`

Updates a module's title and/or description. Description can be Markdown or QuillDelta JSON (auto-detected).

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | no | New module description (Markdown or QuillDelta JSON) |
| `moduleId` | string | yes | The module ID |
| `title` | string | no | New module title |

### `omumu_option_point_batch`

Efficiently set up quiz scoring by creating or updating multiple option points in a single call. If an answer-bucket pair already exists, updates its weight. Use this after creating questions, answers, and buckets to configure the scoring matrix. Example: [{answerId: 'a1', bucketId: 'b1', weight: 2.0}, {answerId: 'a1', bucketId: 'b2', weight: 1.0}]

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `optionPoints` | array | yes | Array of option points, each with: answerId (required), bucketId (required), weight (optional, default 1.0) |

### `omumu_option_point_create`

Creates an option point linking an answer to a bucket with a weight. When a user selects this answer, the weight is added to the bucket's score. After all questions, the bucket with the highest total score becomes the user's result. Example: Answer 'I exercise daily' → Bucket 'Fitness Enthusiast' with weight 2.0.

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `answerId` | string | yes | The answer ID to link |
| `bucketId` | string | yes | The bucket ID to assign points to |
| `weight` | number | no | Point weight (default: 1.0). Higher weights have more influence on final bucket selection. |

### `omumu_option_point_delete`

Removes the link between an answer and a bucket, so selecting this answer no longer contributes to that bucket's score

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `optionPointId` | string | yes | The option point ID to delete |

### `omumu_option_point_list`

Lists all option points for a specific question, showing which answers are linked to which buckets and their weights

- **Write category:** read-only
- **Access:** requires the `QUIZ_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `questionId` | string | yes | The question ID |

### `omumu_option_point_update`

Updates an option point's weight to adjust scoring influence

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `optionPointId` | string | yes | The option point ID |
| `weight` | number | no | New weight value |

### `omumu_question_create`

Creates a new question in a quiz. After creating a question, add answer options with omumu_answer_create. RADIO type shows single-choice radio buttons, CHECKBOX allows multiple selections.

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `header` | string | yes | The question text |
| `quizId` | string | yes | The quiz ID |
| `type` | string | no | Question type: RADIO, CHECKBOX, TEXTAREA, RADIO_OTHER, CHECKBOX_OTHER |

### `omumu_question_update`

Updates a question's header or type

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `header` | string | no | New question text |
| `questionId` | string | yes | The question ID |
| `type` | string | no | New question type |

### `omumu_quiz_create`

Creates a new quiz (question set). Quizzes can optionally link to opt-in forms for email capture: preQuizOptinFormId shows an email capture form before the quiz starts (for non-logged-in users), postQuizOptinFormId shows one after quiz completion. After creating a quiz, add questions with omumu_question_create, then add answer options with omumu_answer_create, create result buckets with omumu_bucket_create, and finally link answers to buckets using omumu_option_point_create.

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `label` | string | yes | The quiz name/label |

### `omumu_quiz_get`

Gets a quiz with optional questions and result buckets. Returns preQuizOptinFormId and postQuizOptinFormId if linked opt-in forms are configured for email capture. To see the complete quiz scoring configuration, set includeQuestions=true, includeBuckets=true, and includeOptionPoints=true. Option points link answers to buckets with weights - the bucket with the highest total weight from selected answers wins.

- **Write category:** read-only
- **Access:** requires the `QUIZ_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `includeBuckets` | boolean | no | Include result buckets |
| `includeOptionPoints` | boolean | no | Include option points (answer-to-bucket scoring weights). Requires questions to be fetched. |
| `includeQuestions` | boolean | no | Include questions and answers |
| `quizId` | string | yes | The quiz ID |

### `omumu_quiz_list`

Lists all quizzes for the current site

- **Write category:** read-only
- **Access:** requires the `QUIZ_READ` permission

No parameters.

### `omumu_quiz_update`

Updates a quiz's properties including label and linked opt-in forms for email capture. Pre-quiz opt-in forms appear before the quiz starts for non-logged-in users. Post-quiz opt-in forms appear after completion. Set to null to remove the link.

- **Write category:** standard write
- **Access:** requires the `QUIZ_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `label` | string | no | New label |
| `postQuizOptinFormId` | string | no | ID of opt-in form to show after quiz completion (for non-logged-in users), or null to remove |
| `preQuizOptinFormId` | string | no | ID of opt-in form to show before quiz (for non-logged-in users), or null to remove |
| `quizId` | string | yes | The quiz ID |

### `omumu_resource_delete`

Permanently deletes a SiteResource. Fails if the resource is currently referenced by any lesson, page, or other entity — remove those references first.

- **Write category:** standard write
- **Access:** requires the `RESOURCE_DELETE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `resourceId` | string | yes | The resource ID to delete |

### `omumu_resource_get`

Returns the full metadata for a single SiteResource, including CDN URI, thumbnail, MIME type, file size, and video processing status (when applicable).

- **Write category:** read-only
- **Access:** requires the `RESOURCE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `resourceId` | string | yes | The resource ID |

### `omumu_resource_list`

Lists uploaded files (videos, images, PDFs, etc.) on the current site. Optionally filter by a MIME type prefix (e.g. 'video/' or 'image/png') and paginate with limit/offset. Returns total count so callers can implement 'load more' paging.

- **Write category:** read-only
- **Access:** requires the `RESOURCE_READ` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | no | Maximum resources to return (default: 25, max: 100) |
| `mimeTypePrefix` | string | no | Optional prefix match against the resource's MIME type, e.g. 'video/' to return all videos or 'image/png' to return only PNG images. Omit to list all types. |
| `offset` | number | no | Number of resources to skip for pagination (default: 0) |

### `omumu_resource_update`

Updates the title and/or description of a SiteResource. Does not change the underlying file — re-upload for that. At least one of title/description must be provided.

- **Write category:** standard write
- **Access:** requires the `RESOURCE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | no | New description (optional) |
| `resourceId` | string | yes | The resource ID |
| `title` | string | no | New title (optional) |

### `omumu_resource_upload_presign`

Prepares the upload of any file type the site accepts (PDF, image, audio, text, office document, archive, video) without the bytes passing through the MCP channel. Check 'uploadProtocol' in the result. 'put': send exactly 'length' bytes of the file as the body of an HTTP PUT to 'uploadUrl' (no Authorization header; the URL is a single-use ticket that expires at 'expiresAt'). The response is 201 with the new resource (id, fileName, fileSize, uri); use its id with omumu_lesson_resource_create or omumu_set_image. A failed PUT needs a new presign. 'tus': a video; follow omumu_resource_upload_video_presign's instructions with the same fields (resourceId is usable right away). Uploads count against the site's per-file and total storage limits.

- **Write category:** standard write
- **Access:** requires the `RESOURCE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `filename` | string | yes | Original file name including extension, e.g. 'worksheet.pdf'. Becomes the resource's file name. |
| `filetype` | string | yes | MIME type of the file, e.g. application/pdf, image/png, audio/mpeg, application/zip, video/mp4 |
| `length` | number | yes | Exact file size in bytes; the upload is rejected if the body differs |

### `omumu_resource_upload_video_presign`

Creates a SiteResource for a video (videoProcessingStatus QUEUED) and returns a presigned TUS (tus.io) upload the caller PATCHes bytes to directly — the video never passes through the MCP server or the JSON-RPC channel. Use the returned resourceId right away with omumu_lesson_resource_create or omumu_set_image; poll omumu_resource_get for videoProcessingStatus once the upload finishes and Bunny's webhook reconciles it. To perform the upload: POST to the returned 'endpoint' with headers AuthorizationSignature, AuthorizationExpire, VideoId (the 'guid' field), LibraryId, Upload-Length (the file size), and Tus-Resumable: 1.0.0 — Bunny responds with a Location header, which is the URL to PATCH the file bytes to (Content-Type: application/offset+octet-stream, Upload-Offset: 0, Tus-Resumable: 1.0.0).

- **Write category:** standard write
- **Access:** requires the `RESOURCE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `filename` | string | yes | Original file name of the video, e.g. 'lesson-1.mp4' |
| `filetype` | string | yes | MIME type of the video: video/mp4, video/webm, video/ogg, video/quicktime, or video/x-matroska |
| `length` | number | yes | Total file size in bytes |

### `omumu_set_image`

Attach an existing image resource to a course, module, or lesson. Use this to reuse an already generated image or to change an entity's image.

- **Write category:** standard write
- **Access:** requires the `COURSE_WRITE` permission

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `resourceId` | string | yes | ID of the image resource to attach |
| `targetId` | string | yes | ID of the entity to set the image on |
| `targetType` | string | yes | Entity type: 'course', 'module', or 'lesson' |
