# Tools · Courses

> MCP tools for courses: listing and viewing courses with their modules and lessons, building a whole course with the product that sells it, editing and deleting the course, adding, editing, moving, reordering and deleting modules and lessons, seeing the students, the A single course mode, and the student's side: enrollment and progress.

Building and maintaining courses: a whole course in one call (space, modules, lessons with their content and the sales product), the cover and the status, and then the modules and lessons: adding, renaming, reordering, moving a lesson to another module and deleting. Students enroll and mark their progress through the conversation.

Each tool carries a badge: <span class="tool-kind tool-kind-read">Read-only</span> changes nothing, <span class="tool-kind tool-kind-write">Changes</span> creates or changes something right away, and <span class="tool-kind tool-kind-confirm">Asks to confirm</span> only acts after your yes to a summary. See [Money and confirmation](/mcp/dinheiro-e-confirmacao).

## In short

| Tool | What it does | Minimum role | Type |
|---|---|---|---|
| [`list_courses`](#list-courses) | The community's courses | Any | <span class="tool-kind tool-kind-read">Read-only</span> |
| [`list_my_courses_in_progress`](#list-my-courses-in-progress) | Your courses in progress and the lesson to continue | Any | <span class="tool-kind tool-kind-read">Read-only</span> |
| [`get_course`](#get-course) | A course with its modules and lessons | Any | <span class="tool-kind tool-kind-read">Read-only</span> |
| [`create_course`](#create-course) | Space, course, modules, lessons and the product, in one go | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`update_course`](#update-course) | Edits a course's title, level, status and cover | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`create_course_module`](#create-course-module) | Adds a module to a course | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`create_lesson`](#create-lesson) | Adds a lesson to a module | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`update_course_module`](#update-course-module) | Renames a module or changes its position | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`update_lesson`](#update-lesson) | Changes a lesson, or moves it to another module | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`list_course_enrollments`](#list-course-enrollments) | A course's students, with their progress | Moderator | <span class="tool-kind tool-kind-read">Read-only</span> |
| [`reorder_course_modules`](#reorder-course-modules) | Puts a course's modules in a new order | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`reorder_lessons`](#reorder-lessons) | Puts a module's lessons in a new order | Admin | <span class="tool-kind tool-kind-write">Changes</span> |
| [`delete_course_module`](#delete-course-module) | Deletes a module with its lessons | Admin | <span class="tool-kind tool-kind-confirm">Asks to confirm</span> |
| [`delete_lesson`](#delete-lesson) | Deletes a lesson | Admin | <span class="tool-kind tool-kind-confirm">Asks to confirm</span> |
| [`delete_course`](#delete-course) | Deletes a course | Admin | <span class="tool-kind tool-kind-confirm">Asks to confirm</span> |
| [`enroll_in_course`](#enroll-in-course) | Enrolls you in a published course | Member (not finance) | <span class="tool-kind tool-kind-write">Changes</span> |
| [`enroll_member_in_course`](#enroll-member-in-course) | Enrolls someone else in a course that is free for them | Moderator | <span class="tool-kind tool-kind-write">Changes</span> |
| [`complete_lesson`](#complete-lesson) | Records your progress in a lesson | Member (not finance) | <span class="tool-kind tool-kind-write">Changes</span> |

## `list_courses`

<span class="tool-kind tool-kind-read">Read-only</span>

**List courses.** The community's courses. *Minimum role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `space_id` | id | No | Only the courses in one courses space |

**Returns:** the courses, with id, slug, title, level and status.

**Ask like this:** *"Which courses already exist?"*

`saved` lists only the courses you saved. Each course carries the instructor, what you will learn, `studentsCount` (only when the API shows it: members, from 10 students up) and whether it is saved.

## `list_my_courses_in_progress`

<span class="tool-kind tool-kind-read">Read-only</span>

**Continue where I left off.** The courses you started and did not finish, most recent first, each with the lesson to open now. *Minimum role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | 1 to 20 | No | How many courses (default 10). `1` answers "where did I stop?" |

**Returns:** `items`, each with the course (`id`, `slug`, `title`, `coverFormat`, `spaceId`), the lesson (the last one you touched or, once it is completed, the next open one, with its module), `resumeAt` (the second in the video where you stopped; `null` without a position), `lastAccessedAt` and the progress (completed lessons, total and percentage). Only published courses, with an active enrollment, in spaces you still open.

**Ask like this:** *"Where did I stop?"* → `list_my_courses_in_progress {"limit":1}`, then `get_course {"course":"<slug>"}` to open the lesson.

**Notes:** the suggested next steps are opening the lesson (`get_course`) and marking it done (`complete_lesson`); with no course in progress, `list_courses`. See [Continue where you left off](/espacos/cursos#continue-where-you-left-off).

## `get_course`

<span class="tool-kind tool-kind-read">Read-only</span>

**View a course with modules and lessons.** A course with its full structure. *Minimum role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | text | Yes | The course id or slug |

**Returns:** the course, the modules (with the id `create_lesson` asks for) and the lessons of each module. The lesson content (`body` and `content_url`) only comes back for who can see it: owner, admin, or an enrolled student. Everyone else gets just the outline (title, type and duration). It also carries `space_mode`, the space's mode (`single` or `multi`), and, for the owner and the admin, how many students have progress in each module and lesson. The enrollment count (`stats.enrollments`) only comes back for the team.

**Ask like this:** *"Show me the structure of the Excel from Scratch course."*

For someone enrolled, each locked module and lesson carries the reason (`lockReason`: `sequence` or `date`), when it opens (`unlocksAt`, in the community's time zone) and a sentence, such as *"Locked: opens October 18, 2026, 12:00 AM"* or *"Locked: finish \"X\" first"*; the locked lesson's content does not come. Moderators and finance read the content without enrolling.

For the team, quiz lessons carry the questions, the options and the answer key; the student receives the prompts and options, never the key.

Carries `userFinalGrade` and explains `BELOW_MIN_GRADE` when the final grade is below the certificate minimum.

Each lesson carries `hasMedia` and, for whoever reads the content, the name, size and type of the hosted file; the playback link comes from `get_lesson_media_url`.

A Bunny Stream or Cloudflare Stream lesson carries `videoEmbed`, with the signed `src`, or `unavailable` when the provider refused the key. See [`list_video_keys`](/mcp/ferramentas-comunidade#list-video-keys).

## `create_course`

<span class="tool-kind tool-kind-write">Changes</span>

**Create a complete course.** Courses space → course → modules → lessons → (optional) the product that unlocks the space. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `title` | text, up to 255 | Yes | The course title |
| `description` | plain text | No | No formatting: Markdown marks are stripped |
| `level` | `beginner`, `intermediate`, `advanced` | No | Default: `beginner` |
| `space_id` | id | No | An existing Courses space |
| `section_id` | id | No | Where to create the space, if `space_id` isn't given |
| `mode` | `single`, `multi` | No | The mode of the space the call creates: `multi` (list of courses, the default) or `single` (A single course). See [A single course](#a-single-course) |
| `space_title` | text | No | Default: the course title |
| `icon` | one of the icons in the list | No | The icon of the space it creates. See [Icons](/mcp/ferramentas-comunidade#icons) |
| `image_url` | link | No | The course cover. See [Images](/mcp/ferramentas#images) |
| `cover_format` | `BANNER`, `POSTER`, `VIDEO` | No | The cover format: **Banner** (3:1, default), **Poster** (2:3, for the Showcase) or **Video** (16:9). See [Cover format](/espacos/cursos#cover-format) |
| `modules` | list, up to 30 | No | Each with `title` (required) and `lessons` |
| `sale` | object | No | The product that sells the course (see below) |
| `certificate` | object | No | `{enabled, template_id, text}`: the certificate the course issues on completion. See [Certificate on completion](#certificate-on-completion) |
| `completion_badge` | badge id or name, or `null` | No | The [badge](/membros/engajamento#badges) whoever finishes the course gets, without a workflow; `null` removes it. Owner and admin only |
| `idempotency_key` | text, up to 100 | No | |

Each **module** has `title` and `lessons`, a list of up to 50, and each **lesson** has `title` (required), `type` (`text`, `video`, `link`, `image`; default `text`), `duration` (`mm:ss` or `hh:mm:ss`, e.g. *12:30*), and the content: `body` (Markdown) for a text lesson, and `content_url` for video, link and image lessons.

The **sale** (and the **ticket**, in `create_event`):

| Field | Type | Description |
|---|---|---|
| `price` | number, in reais | Required |
| `title` | text, 3 to 255 | Default: the course title (or *Ingresso — event title*) |
| `type` | `ONE_TIME`, `INSTALLMENT`, `SUBSCRIPTION` | Default: `ONE_TIME` (one-time) |
| `interval` | `MONTHLY`, `YEARLY` | Only for `SUBSCRIPTION` |
| `max_installments` | 1 to 12 | Up to how many card installments |
| `stock` | number | Quantity on sale |
| `direct_link` | yes or no | Sold by direct link |
| `publish` | yes or no | Put on sale right away. Default: draft |

**Returns:** the space (and whether it was just created), the course (id, title, slug, status), the modules with their lessons, and the product (id, title, price, status), if any.

**Ask like this:** *"Build the Excel from Scratch course in the courses space, with the modules Basics, Formulas and Pivot tables, 3 video lessons in each, and a US$ 97 product, as a draft."*

Every course is created with its own product (**Course** kind, free draft), and the response carries `product`. `sale` now **sets up that product** (price, seats) instead of creating another, and it only goes live with `sale.publish: true` (a price above zero requires approved Business Information; free doesn't). The response suggests the next steps: **set the price** (`update_product`, `update_price`) and **publish** (`publish_product`). See [Selling the course](/espacos/cursos#selling-the-course).

**Notes:** a text lesson needs `body`; the others need `content_url` (starting with `http://` or `https://`). Video only from YouTube, Vimeo, Loom, Bunny Stream, Cloudflare Stream, Panda Video or HLS (`.m3u8`); from any other provider, use `type: link`. The API checks the lesson before creating it and returns the reason on the right field. The product unlocks the course space for whoever buys it. In a space in **A single course** mode that already has a course, the API refuses a second one (drafts and archived courses count), with the guidance to edit the existing course or switch the space to a list of courses; the composite tool undoes what it created.

`require_sequential` turns on lessons in sequence, and each module of the course may have `available_at` (ISO 8601 with time zone) **or** `available_after_days` (days after enrollment), never both.

Accepts `instructor` (a profile that is owner, admin, moderator or finance), `learning_points` (up to 8 items) and `leaderboard_enabled`, owner and admin only.

`certificate_min_grade` (0 to 100), owner and admin only.

## `update_course`

<span class="tool-kind tool-kind-write">Changes</span>

**Edit a course.** Changes the title, description, level, status (`draft`, `published`, `archived`) and the cover. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id, slug or link | Yes | |
| `title`, `description` | text | No | |
| `level` | `beginner`, `intermediate`, `advanced` | No | |
| `status` | `draft`, `published`, `archived` | No | |
| `image_url` or `image_base64` | link or file | No | The new cover |
| `cover_format` | `BANNER`, `POSTER`, `VIDEO` | No | The cover format: **Banner** (3:1, default), **Poster** (2:3, for the Showcase) or **Video** (16:9). See [Cover format](/espacos/cursos#cover-format) |
| `certificate` | object | No | `{enabled, template_id, text}`: the certificate the course issues on completion. See [Certificate on completion](#certificate-on-completion) |
| `completion_badge` | badge id or name, or `null` | No | The [badge](/membros/engajamento#badges) whoever finishes the course gets, without a workflow; `null` removes it. Owner and admin only |

**Ask like this:** *"Publish the Excel from Zero course and swap the cover for this one."* · *"Use a poster cover on the Excel from Scratch course."* → `update_course {"course":"<slug>","cover_format":"POSTER"}`

**Notes:** modules and lessons change with [`update_course_module`](#update-course-module), [`update_lesson`](#update-lesson), the order tools and the delete tools. On publishing, the assistant suggests looking at the students (`list_course_enrollments`).

`require_sequential` turns lessons in sequence on or off (owners and admins only).

Accepts `instructor` (and `remove_instructor`), `learning_points` and `leaderboard_enabled` (owner and admin only).

`certificate_min_grade` (0 to 100; `null` clears it and, when omitted, it is kept), owner and admin only.

## `create_course_module`

<span class="tool-kind tool-kind-write">Changes</span>

**Add a module to a course.** Adds a module to the end of an existing course. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course_id` | id | Yes | From `list_courses` or `create_course` |
| `title` | text | Yes | |

**Returns:** the new module, with the id `create_lesson` needs.

**Ask like this:** *"Add a Charts module to the Excel from Scratch course."*

**Notes:** to rename it or change its position later, [`update_course_module`](#update-course-module).

`available_at` (fixed date, ISO 8601 with time zone; without one, the community's wall-clock time applies) **or** `available_after_days` (from 0, which opens at once, up), never both.

## `create_lesson`

<span class="tool-kind tool-kind-write">Changes</span>

**Add a lesson to a module.** Adds a lesson to the end of a module. *Minimum role: admin.* A video lesson also takes `video_media_id`, a Drop the person recorded (see [`upload_lesson_drop`](#upload-lesson-drop)), instead of the link or the file.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `module_id` | id | Yes | From `get_course` |
| `title` | text | Yes | |
| `type` | `text`, `video`, `link`, `image` | No | Default: `text` |
| `duration` | text | No | `mm:ss` or `hh:mm:ss`, e.g. *12:30* |
| `body` | Markdown text | No | The text of a text lesson |
| `content_url` | link | No | Video (YouTube, Vimeo, Loom, Bunny Stream, Cloudflare Stream, Panda Video or HLS (`.m3u8`) only), link or image; `http` or `https` |

**Returns:** the new lesson.

**Ask like this:** *"In the Charts module, create the lessons Line charts (8 min) and Pie charts (6 min), both video."*

**Notes:** a text lesson needs `body`; video, link and image lessons need `content_url`. Video only from YouTube, Vimeo, Loom, Bunny Stream, Cloudflare Stream, Panda Video or HLS (`.m3u8`): from anywhere else, ask for `type: link`. The student only sees the content after enrolling. After creating the lesson, the assistant suggests publishing the course (`update_course`).

`quiz` (a quiz-type lesson): `quiz.pass_percent` and `quiz.questions[]`, each with `options[]` and `is_correct` (just one per question). Owner and admin only.

`quiz.max_attempts` (1 to 100; empty = unlimited).

`type: file` creates a file lesson, and `awaiting_media` creates a video lesson with no link, as "content coming soon", to receive the file with `upload_lesson_media`.

With the signing key saved ([`update_video_key`](/mcp/ferramentas-comunidade#update-video-key)), Bunny Stream and Cloudflare Stream videos play signed.

## `update_course_module`

<span class="tool-kind tool-kind-write">Changes</span>

**Edit course module.** Renames a module or changes its position. Only what you send changes. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `module_id` | id | Yes | From `get_course` |
| `title` | text, up to 255 | No | |
| `order` | number, from 0 | No | The position in the course |

**Ask like this:** *"Rename the Formulas module to Essential formulas and make it second."*

Changes when the module opens with `available_at` or `available_after_days` (the one sent replaces the other; `null` clears the rule). Both together are refused.

## `update_lesson`

<span class="tool-kind tool-kind-write">Changes</span>

**Edit lesson.** Changes the title, type, duration, content or position of a lesson, or moves it to another module of the same course. Only what you send changes. *Minimum role: admin.* A video lesson also takes `video_media_id`, a Drop the person recorded (see [`upload_lesson_drop`](#upload-lesson-drop)), instead of the link or the file.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `lesson_id` | id | Yes | From `get_course` |
| `title` | text, up to 255 | No | |
| `type` | `text`, `video`, `link`, `image` | No | |
| `duration` | text | No | `mm:ss` or `hh:mm:ss`, e.g. *12:30* |
| `body` | Markdown text | No | The text of a text lesson |
| `content_url` | link | No | Video (YouTube, Vimeo, Loom, Bunny Stream, Cloudflare Stream, Panda Video or HLS (`.m3u8`) only), link or image; `http` or `https` |
| `order` | number, from 0 | No | The position in the module |
| `module_id` | id | No | Moves the lesson to this module (of the same course). Without `order`, it goes to the end |

**Ask like this:** *"Move the Pie chart lesson to the Fundamentals module, as the last lesson."*

**Notes:** the API checks the lesson as it will be: when you change the `type`, also send the content that goes with it.

`quiz` (a quiz-type lesson): `quiz.pass_percent` and `quiz.questions[]`, each with `options[]` and `is_correct` (just one per question). Owner and admin only.

`quiz.max_attempts` (1 to 100); `null` clears the limit and, when omitted, it is kept.

## `list_course_enrollments`

<span class="tool-kind tool-kind-read">Read-only</span>

**List course students.** Who is enrolled in a course, with each one's progress: the dashboard's **Students** tab. *Minimum role: moderator (read-only). Members don't even see the tool.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id or slug | Yes | The course |
| `page` | number | No | Page, starting at 1 |
| `limit` | number | No | Items per page (up to 100) |

**Returns:** the enrolled students and those who completed, from the most recent enrollment to the oldest, each with their profile, status, completed lessons and total lessons (the ones that exist today), progress percentage and last activity. A canceled enrollment does not count, and the total is the same student number as in the course list.

**Ask like this:** *"Who is taking the Excel from Scratch course? Who has already finished?"*

**Notes:** with an empty list, the assistant suggests checking whether the course is published and has lessons (`get_course`).

Each student carries `finalGrade`, the course final grade.

## `reorder_course_modules`

<span class="tool-kind tool-kind-write">Changes</span>

**Reorder course modules.** Records the order of all the course's modules at once. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course_id` | id | Yes | From `list_courses` |
| `module_ids` | list of ids | Yes | **All** of the course's modules, in the new order |

**Ask like this:** *"Put Formulas before Basics in the Excel from Scratch course."*

**Notes:** the list must be exactly what exists. If a module is missing or another is repeated, the call returns an error and nothing changes. To change the position of just one, [`update_course_module`](#update-course-module).

## `reorder_lessons`

<span class="tool-kind tool-kind-write">Changes</span>

**Reorder module lessons.** Records the order of all the lessons of a module at once. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `module_id` | id | Yes | From `get_course` |
| `lesson_ids` | list of ids | Yes | **All** of the module's lessons, in the new order |

**Ask like this:** *"In the Basics module, put the video lesson before the text one."*

**Notes:** same rule as `reorder_course_modules`: the complete list, or nothing changes. To move a lesson to another module, [`update_lesson`](#update-lesson) with `module_id`.

## `delete_course_module`

<span class="tool-kind tool-kind-confirm">Asks to confirm</span>

**Delete course module.** Deletes a module and all its lessons. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `module_id` | id | Yes | From `get_course` |
| `course` | id, slug or link | No | The course, so the summary shows the names of the module and its lessons |

**Notes:** the progress of whoever already watched is kept.

## `delete_lesson`

<span class="tool-kind tool-kind-confirm">Asks to confirm</span>

**Delete lesson.** *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `lesson_id` | id | Yes | From `get_course` |
| `course` | id, slug or link | No | The course, so the summary shows the lesson's name |

**Notes:** the progress of whoever already watched is kept.

## `delete_course`

<span class="tool-kind tool-kind-confirm">Asks to confirm</span>

**Delete course.** The course is archived and leaves the feed. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id, slug or link | Yes | |

**Returns:** on the first call, the title, how many modules and enrollments the course has, and the confirmation code.

Deleting the course also archives its product, and is refused (`409`) while there's a paid purchase that still gives access: refund it first.

## `enroll_in_course`

<span class="tool-kind tool-kind-write">Changes</span>

**Enroll in course.** Enrolls you in a published course. *Role: owner, admin, moderator or member.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id, slug or link | Yes | |

**Ask like this:** *"Enroll me in the Excel from Scratch course."*

**Notes:** enrolling again is not an error. If you were already enrolled (enrollment in progress or completed), the answer carries `already_enrolled: true`, with no extra points. When restarting an enrollment that had been canceled, it comes back `false`. Once enrolled, `get_course` shows the lesson content.

A course with its product published and priced answers `403` to whoever hasn't bought it, has no plan that includes it and wasn't already in, and the response carries the price (the purchase is made in the app: `open_link`). A full course answers `409` (sold out) and the assistant offers [`join_waitlist`](#join-waitlist). A free course is obtained with [`claim_free_product`](#claim-free-product).

## `enroll_member_in_course`

<span class="tool-kind tool-kind-write">Changes</span>

**Enroll someone in the course.** The team enrolls a person from the community in a published course that doesn't charge them, with the rules of the person's own enrollment (enrolling again returns the existing enrollment, with no extra points; seats). Runs on the first call. *Role: owner, admin or moderator.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id or link | Yes | The course |
| `member` | id, e-mail or profile link | Yes | The person |

**Ask like this:** *"Enroll João in the Onboarding course."*

## Seats, waitlist and free product

The five tools below take a `product_id`: it is the `product.id` that `get_course`, `enroll_in_course` and `rsvp_event` return, or the id of a product from `list_products`. The tools sit on this page because they count under **Courses**, but they apply to courses, events, downloads and one-off products. See [Seats and waitlist](/monetizacao/produtos#seats-and-waitlist) and [Free product](/monetizacao/produtos#free-product).

| Tool | What it does | Minimum role | Type |
|---|---|---|---|
| [`join_waitlist`](#join-waitlist) | Joins the waitlist of a sold-out product | Any | <span class="tool-kind tool-kind-write">Changes</span> |
| [`leave_waitlist`](#leave-waitlist) | Leaves the waitlist | Any | <span class="tool-kind tool-kind-write">Changes</span> |
| [`my_waitlist_position`](#my-waitlist-position) | Your place in line | Any | <span class="tool-kind tool-kind-read">Read-only</span> |
| [`list_waitlist`](#list-waitlist) | Who is waiting, in order | Moderator or finance | <span class="tool-kind tool-kind-read">Read-only</span> |
| [`claim_free_product`](#claim-free-product) | Gets a free product | Any | <span class="tool-kind tool-kind-write">Changes</span> |

### `join_waitlist`

<span class="tool-kind tool-kind-write">Changes</span>

**Join the waitlist.** For a sold-out product (a full course or event, or a product with limited stock). One place per person and product: asking again returns the same place. With a free seat, the tool refuses (`409`) and tells you to enroll, confirm attendance or buy. When a seat frees up, the next person gets a **24-hour offer** by e-mail with the seat reserved and takes the normal action. *Role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `product_id` | id | Yes | The course's or event's `product.id`, or a product from `list_products` |

**Ask like this:** *"The Excel from Scratch course is full: put me on the waitlist."*

### `leave_waitlist`

<span class="tool-kind tool-kind-write">Changes</span>

**Leave the waitlist.** If you had an open offer, the seat goes to the next person. You can come back (at the end of the line), so it doesn't ask to confirm. *Role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `product_id` | id | Yes | |

### `my_waitlist_position`

<span class="tool-kind tool-kind-read">Read-only</span>

**Your place in line.** `status` (`WAITING` or `OFFERED`), `position` (0 = you have an offer) and when the offer expires; `place` comes back null if you're not in line. Never someone else's place. *Role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `product_id` | id | Yes | |

### `list_waitlist`

<span class="tool-kind tool-kind-read">Read-only</span>

**Who is waiting.** In order: position (0 = has an open 24-hour offer), status, join and offer dates, and the person. *Minimum role: moderator (or finance).*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `product_id` | id | Yes | |

### `claim_free_product`

<span class="tool-kind tool-kind-write">Changes</span>

**Get a free product.** An item priced at zero (a free download, a free plan, a free course or event): no payment, no confirmation, with access at once. Once per person and product; asking again returns what you already have. A priced product is refused (the purchase is made in the app), and a sold-out one answers `409`. *Role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `product_id` | id | Yes | |
| `shipping_address` | object | Only for a physical product | All the delivery address fields; the postal code is checked against the country. Ask the person for each field first |

**Ask like this:** *"Get me the free cash flow spreadsheet."*

## `complete_lesson`

<span class="tool-kind tool-kind-write">Changes</span>

**Record lesson progress.** Marks a lesson as completed, or records a percentage, in a course you're enrolled in. *Role: owner, admin, moderator or member.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id, slug or link | Yes | |
| `lesson_id` | id | Yes | From `get_course` |
| `completed` | yes or no | No | Default yes |
| `progress_percent` | from 0 to 100 | No | |

**Ask like this:** *"Mark the Basic formulas lesson as watched."*

**Notes:** `completed: false` only records that you opened the lesson; it never undoes a completed lesson. The result says whether the course is complete and your progress. When you finish the course, the assistant suggests looking at your certificates (`list_my_certificates`).

Completing a lesson still locked by the sequence or a date is refused with `409`, with the reason and when it opens; the lesson gets no progress or points.

## Certificate on completion

| Tool | What changes |
|---|---|
| `create_course`, `update_course` and `create_space` (with `course`) | Accept `certificate`: `enabled` (on or off), `template_id` (an active template) and `text` (plain text, up to 5,000 characters). When turning it on, the reply warns that **whoever completes from now on gets it by email, and whoever already completed does not**. A missing block leaves things as they are; an empty `template_id` goes back to the platform default; an empty `text` goes back to the default text |
| `get_course` and `list_courses` | Owner and admin see the course's `certificate`. If the template (or the default) is archived, there is a warning that **the course issues nothing** and a suggestion to pick another. Whoever completed sees their `userCertificate` (being generated or ready) |
| `list_certificate_templates` | Without `space`, owner and admin get the **active templates of every space**, platform default first, each with its space of origin. Accepts `q` to search by name. The moderator must give the space |
| `complete_lesson` | On completing a course that issues, it says the certificate arrives by email in a moment and suggests `list_my_certificates`. A course that does not issue suggests nothing |

API errors come back as they are: `certificate.templateId` (template from another community, or archived) and `certificate.text`. See recipes [25](/mcp/receitas#25-set-up-this-course-s-certificate), [26](/mcp/receitas#26-what-does-the-course-issue) and [27](/mcp/receitas#27-i-finished-the-course).

## A single course

The Courses space has two modes: **list of courses** (`multi`, the default) and **A single course** (`single`), where the space is one course and whoever opens it lands straight on it. See [Courses](/espacos/cursos#courses-in-a-single-course-mode).

- [`create_space`](/mcp/ferramentas-comunidade#create-space) accepts `mode` and, in `single`, `course` (`title`, `level`, `description`): the space and the course, as a draft, are created in one call. `course` on its own already implies `single`.
- [`update_space`](/mcp/ferramentas-comunidade#update-space) switches the mode. From list to `single` with 2 or more courses, Memberfy refuses and says how many courses there are and that you need to delete them until one is left (drafts and archived courses count); nothing is deleted. From `single` to list always goes through.
- `create_course` with `mode` applies to the space the call creates; with `space_id`, the existing space keeps its mode. [`list_spaces`](/mcp/ferramentas-comunidade#list-spaces) and `get_course` show the mode.
- A second course in a `single` space is refused, with the API's message.

## `leave_course`

<span class="tool-kind tool-kind-write">Changes</span>

**Leave course.** The student leaves a course they are enrolled in; progress is kept and they can come back with `enroll_in_course`. Whoever completed the course cannot leave (`409`). Reversible, so it asks no confirmation. *Role: whoever is enrolled.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id, slug or link | Yes | |

**Ask like this:** *"Leave the Excel from Scratch course; I'll come back later."*

## `favorite_course`

<span class="tool-kind tool-kind-write">Changes</span>

**Save course.** Saves a course for later, or takes it off the saved list with `saved=false`. It is personal: nobody else sees it. *Role: anyone with access to the space.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id, slug or link | Yes | |
| `saved` | yes or no | No | Default: yes. `false` removes it from saved |

**Ask like this:** *"Save the SQL course so I can see it later."*

## `submit_quiz_attempt`

<span class="tool-kind tool-kind-write">Changes</span>

**Submit a quiz's answers.** Sends all the answers of a quiz lesson; the server grades. If passed, the lesson is completed and the result carries the correct answers; if failed, it carries only the score. The team tests without recording anything. Past 30 attempts per hour, the answer warns. *Role: whoever is enrolled, or the team.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id or slug | Yes | |
| `lesson_id` | id | Yes | From `get_course` |
| `answers` | list | Yes | One answer per question: `question_id` and `option_id`, from `get_course` |

**Ask like this:** *"Answer the SQL lesson quiz: question 1, option B; question 2, option A."*

The answer carries `attemptsUsed`, `attemptsLeft`, `bestScorePercent` and `closedWithoutPassing`; an attempt after they run out comes back as `409` with the reason.

## `list_quiz_attempts`

<span class="tool-kind tool-kind-read">Read-only</span>

**See my attempts.** The last 10 attempts at a quiz, with score, result and date. *Role: whoever is enrolled.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id or slug | Yes | |
| `lesson_id` | id | Yes | |

**Ask like this:** *"How did my attempts at the SQL quiz go?"*

## `upload_lesson_media`

<span class="tool-kind tool-kind-write">Changes</span>

**Host a video or file in a lesson.** Sends the file by **public link** (`file_url`) or as **base64** (`file_base64`, up to 25 MB; for more, use the link). The lesson (`lesson_id`, from `get_course`) must be video (MP4, WebM or MOV, up to 500 MB) or file (PDF, ZIP, Word, Excel, PowerPoint, PNG, JPEG, GIF or WebP, up to 50 MB). The same checks as the app apply (type, size and content). Uploading again replaces the media. At the plan's storage limit, a new upload is refused. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `lesson_id` | id | Yes | From `get_course` |
| `file_url` | link | One of the two | Public address of the video or file |
| `file_base64` | text | One of the two | The file in base64, up to 25 MB |
| `mime_type` | text | No | E.g. `video/mp4` or `application/pdf` |
| `name` | text | No | The name shown to the student |

**Ask like this:** *"Host this PDF in the Support material lesson: https://…/guide.pdf"*

## `upload_lesson_drop`

<span class="tool-kind tool-kind-write">Changes</span>

**Put a Drop on a lesson.** Attaches a Drop to the video lesson (`lesson_id`): the recorded file (`video_file_url` or `video_file_base64`, MP4, WebM or MOV, up to 350 MB) or the `media_id` of a lesson video the person already uploaded. It replaces the lesson's link or file. Right after, Memberfy measures the video (a lesson without a duration gets its duration) and takes up to 6 cover frames (`set_video_cover_frame`). The lesson text comes from `create_drop_description` with `kind: lesson`. *Minimum role: admin.*

## `remove_lesson_media`

<span class="tool-kind tool-kind-confirm">Asks to confirm</span>

**Remove a lesson's media.** Deletes the hosted video or file, gives the space back and leaves the lesson as *"Content coming soon"*. It asks for confirmation: the first call shows the summary and only the second, with the `confirmation_token`, removes. *Minimum role: admin.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `lesson_id` | id | Yes | From `get_course` |

**Ask like this:** *"Take the video off lesson 3; I will upload another."*

## `get_lesson_media_url`

<span class="tool-kind tool-kind-read">Read-only</span>

**Link to watch or download.** A temporary link to a lesson's video or file. Only enrolled students (course published, lesson unlocked by sequence and date) and the team get it; anyone else is refused. The link lasts **1 hour** for video and **5 minutes** for a file: ask for another when it expires, and do not store or share it. *Role: any.*

| Parameter | Type | Required | Description |
|---|---|---|---|
| `course` | id or slug | Yes | |
| `lesson_id` | id | Yes | From `get_course` |

**Ask like this:** *"Give me the link to lesson 2's video."*

## Lesson ratings

`rate_lesson` rates a lesson from 1 to 5, with an optional comment (a student, only their own); `get_course_ratings` gives the team the average, the distribution and the comments per lesson and for the course. `courses_report` gains `averageRating` and `ratingsCount`.

## Related

- [Tools](/mcp/ferramentas)
- [Recipes](/mcp/receitas)
- [Courses](/espacos/cursos)
- [Money and confirmation](/mcp/dinheiro-e-confirmacao)
