> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vacademy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Courses

> Course content, drip rules, invite links and pricing.

Each tool below is one switch in **Settings → MCP Server**. Your AI app sees a tool only when it is switched on for your role; every call is checked again on the server.

## `courses`

**Courses: view** · Read only

See courses: their chapters and slides, slide content, drip rules, invite links and prices, and a quality check of a course before it is submitted.

| Action | What it does |
| :- | :- |
| `list` | courses with status, depth and slide count. |
| `get` | the course tree with chapter / slide ids, types and statuses, plus its batches (session × level) — content is shared by all batches. |
| `get_slide` | one slide's full content. |
| `schema` | READ THIS BEFORE BUILDING — the outline JSON for course\_edit(create\_course), every slide type's fields, the HTML document rules and the quiz format. |
| `brief_checklist` | what to ask the admin first. Interview them one question at a time; never invent prices, dates, links or facts about the institute. |
| `review` | deterministic quality check — run it before telling the admin the course is ready and fix every `fix` item. |
| `drip` | the course's drip rules and whether the institute enforces them. |
| `invites` | its invite links, prices and which one is the default. |
| `get_invite` | one invite in full — landing copy, images, redirect path, success page, its form fields (field\_id, label, type, required, locked) and available\_fields (the institute's fields not yet on the form) for course\_invites\_edit(update\_invite / update\_form\_fields). |
| `payment_setup` | active payment gateways and existing payment plans. |
| `sessions_levels` | the institute's existing sessions (e.g. '2026-27') and levels (e.g. 'Class 9') to reuse by id when a course is taught in several batches (session × level). |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `list`, `get`, `get_slide`, `schema`, `brief_checklist`, `review`, `drip`, `invites`, `get_invite`, `payment_setup`, `sessions_levels`.
  </ParamField>

  <ParamField path="course_id" type="string">
    —
  </ParamField>

  <ParamField path="slide_id" type="string">
    —
  </ParamField>

  <ParamField path="invite_id" type="string">
    —
  </ParamField>

  <ParamField path="batch_id" type="string">
    get: read the tree through this batch (default: the course's first).
  </ParamField>

  <ParamField path="status" type="string">
    list: DRAFT, IN\_REVIEW or ACTIVE.
  </ParamField>

  <ParamField path="search" type="string">
    list: part of the course name.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Read the institute's courses and the contract for building one. YOU (the AI) write every outline and slide; the server only validates and saves — no AI credits are used. Pick an `action`:
  - list (status?: DRAFT|IN_REVIEW|ACTIVE, search?): courses with status, depth and slide count.
  - get (course_id, batch_id?): the course tree with chapter / slide ids, types and statuses, plus its batches (session × level) — content is shared by all batches.
  - get_slide (slide_id): one slide's full content.
  - schema: READ THIS BEFORE BUILDING — the outline JSON for course_edit(create_course), every slide type's fields, the HTML document rules and the quiz format.
  - brief_checklist: what to ask the admin first. Interview them one question at a time; never invent prices, dates, links or facts about the institute.
  - review (course_id): deterministic quality check — run it before telling the admin the course is ready and fix every `fix` item.
  - drip (course_id): the course's drip rules and whether the institute enforces them.
  - invites (course_id): its invite links, prices and which one is the default.
  - get_invite (course_id, invite_id): one invite in full — landing copy, images, redirect path, success page, its form fields (field_id, label, type, required, locked) and available_fields (the institute's fields not yet on the form) for course_invites_edit(update_invite / update_form_fields).
  - payment_setup: active payment gateways and existing payment plans.
  - sessions_levels: the institute's existing sessions (e.g. '2026-27') and levels (e.g. 'Class 9') to reuse by id when a course is taught in several batches (session × level).
  ```
</Accordion>

## `course_drip_edit`

**Courses: drip rules** · Makes changes · Only touches courses that are not live yet.

Let the connected AI app set content release (drip) rules on courses that are not live yet. Never changes the institute-wide drip switches.

<Note>
  **What keeps it safe:** not-live only: drip rules of DRAFT / IN\_REVIEW courses; never flips the institute switches
</Note>

| Action | What it does |
| :- | :- |
| `set_rules` | add or replace the rule on each item. level: package (the whole course — `target` says which level its rules apply to: chapter or slide), chapter or slide (id from courses(get)). behavior: lock (visible, locked — default), hide, or both. |
| `schedule` | one chapter (or slide) per interval, counted from each learner's enrollment. Replaces earlier rules on those items. |
| `remove_rules` | |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `set_rules`, `schedule`, `remove_rules`.
  </ParamField>

  <ParamField path="course_id" type="string" required>
    —
  </ParamField>

  <ParamField path="items" type="object[]">
    set\_rules / remove\_rules: \[\{level, id?, target?, behavior?, rules?}]
  </ParamField>

  <ParamField path="level" type="string">
    schedule: what is released. One of: `chapter`, `slide`.
  </ParamField>

  <ParamField path="start_day" type="integer">
    —
  </ParamField>

  <ParamField path="interval_days" type="integer">
    —
  </ParamField>

  <ParamField path="unlock_time" type="string">
    HH:mm
  </ParamField>

  <ParamField path="anchor" type="string">
    One of: `enrollment`, `session_start`.
  </ParamField>

  <ParamField path="behavior" type="string">
    One of: `lock`, `hide`, `both`.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Set drip (content release) rules on a course that is not live yet (DRAFT / IN_REVIEW). Read courses(action='drip') first. Pick an `action`:
  - set_rules (course_id, items:[{level, id?, target?, behavior?, rules:[RULE]}]): add or replace the rule on each item. level: package (the whole course — `target` says which level its rules apply to: chapter or slide), chapter or slide (id from courses(get)). behavior: lock (visible, locked — default), hide, or both.
  - schedule (course_id, level: chapter|slide, start_day?=1, interval_days?=1, unlock_time?='00:00', anchor?: enrollment|session_start, behavior?): one chapter (or slide) per interval, counted from each learner's enrollment. Replaces earlier rules on those items.
  - remove_rules (course_id, items:[{level, id?}]).
  RULE = {type, params}:
    date_based      {unlock_date: ISO date-time}
    relative_date   {unlock_on_day: int ≥1 (day 1 = enrollment day), anchor?: enrollment|session_start, unlock_time?: 'HH:mm'}
    sequential      {requires_previous: true, threshold: 0-100 (% of the previous item completed)}
    prerequisite    {required_chapters?: [id], required_slides?: [id], threshold: 0-100}
    completion_based {metric: average_of_last_n|average_of_all, count?: int (for last_n), threshold: 0-100}
  All rules of one item must pass. Time rules always apply; progress rules (sequential, prerequisite, completion_based) apply only when the institute enables 'apply configured rules' in course settings.
  ```
</Accordion>

## `course_edit`

**Courses: build drafts** · Makes changes · Drafts only — nothing goes live until someone publishes it in the admin portal.

Let the connected AI app build courses — outline, reading pages, YouTube videos, PDFs, quizzes, questions and assignments — and publish slides when you ask it to. Courses are saved as DRAFT and go live only when an admin approves them in the dashboard. It can also set a course's preview image, banner and intro video, on live courses too. The AI app writes the content itself, so it uses no AI credits.

<Note>
  **What keeps it safe:** draft-only: creates DRAFT courses and edits only DRAFT courses; slides are DRAFT unless the admin asks to publish them (publish\_slides); a course goes live only through dashboard approval. Exception by the admin's choice: update\_media sets a course's images on any course and only fills invite image slots that are empty
</Note>

| Action | What it does |
| :- | :- |
| `create_course` | creates a DRAFT course with its structure. Slides inside chapters may be full slide specs (saved now) or \{type, title} placeholders (returned in `todo` for add\_slide). Returns every chapter id. Optional sessions / levels make one batch per session × level (see courses(action='schema') → batches; reuse existing ones from courses(action='sessions\_levels')); leave them out for a simple course. The admin creating it is set as the course's instructor. |
| `add_chapter` | another chapter (depth ≥ 3). |
| `add_slide` | one slide. |
| `update_slide` | replace a slide of a DRAFT course (same type). |
| `reorder` | the chapter's full slide order. |
| `import_image` | public https image → institute media URL to use in document HTML. |
| `import_pdf` | public https PDF → file\_id for a \{type:'pdf'} slide. |
| `discard_slide` | remove a slide of a DRAFT course. |
| `publish_slides` | publish DRAFT slides (all of the course, one chapter, or the listed ones) — ONLY when the admin asks to publish / approve slides. On a live course learners see them immediately, so confirm with the admin first. |
| `submit_for_review` | send the finished course for approval; an admin approves it in the dashboard and it goes live. Run courses(action='review') first. |
| `update_media` | set or replace the course's images — works on live courses too. The course's invite links get the new images wherever they have none of their own. |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `create_course`, `add_chapter`, `add_slide`, `update_slide`, `reorder`, `import_image`, `import_pdf`, `discard_slide`, `publish_slides`, `submit_for_review`, `update_media`.
  </ParamField>

  <ParamField path="course" type="object">
    create\_course: course metadata (name required).
  </ParamField>

  <ParamField path="copy_from" type="object">
    create\_course: \{course\_id, batch\_id?} — copy an existing course's content into the new course, then extend it.
  </ParamField>

  <ParamField path="chapters" type="object[]">
    create\_course depth 2/3: \[\{name, slides:\[…]}].
  </ParamField>

  <ParamField path="modules" type="object[]">
    create\_course depth 4: \[\{name, chapters:\[…]}].
  </ParamField>

  <ParamField path="subjects" type="object[]">
    create\_course depth 5: \[\{name, modules:\[…]}].
  </ParamField>

  <ParamField path="sessions" type="any[]">
    create\_course: sessions → batches, \[\{name | id, start\_date?, levels?:\[name | \{id}]}]. Omit for a course without sessions.
  </ParamField>

  <ParamField path="levels" type="any[]">
    create\_course: levels (names or \{id}); with no sessions, one batch per level. Omit for a course without levels.
  </ParamField>

  <ParamField path="course_id" type="string">
    —
  </ParamField>

  <ParamField path="chapter_id" type="string">
    —
  </ParamField>

  <ParamField path="module_id" type="string">
    add\_chapter (depth ≥ 4): the module to add it to.
  </ParamField>

  <ParamField path="slide_id" type="string">
    —
  </ParamField>

  <ParamField path="slide_ids" type="string[]">
    —
  </ParamField>

  <ParamField path="name" type="string">
    add\_chapter: chapter name.
  </ParamField>

  <ParamField path="slide" type="object">
    add\_slide / update\_slide: the slide spec (may carry its own status).
  </ParamField>

  <ParamField path="status" type="string">
    Slide status for create\_course / add\_slide / update\_slide. Default DRAFT; PUBLISHED only when the admin asks for published slides. One of: `DRAFT`, `PUBLISHED`.
  </ParamField>

  <ParamField path="position" type="integer">
    add\_slide: 1-based position (default: last).
  </ParamField>

  <ParamField path="url" type="string">
    import\_image / import\_pdf: public https URL.
  </ParamField>

  <ParamField path="media" type="object">
    create\_course / update\_media: MEDIA \{preview?, banner?, intro?}.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Build courses as DRAFTS. You write the outline and every slide yourself (read courses(action='schema') first); this tool validates and saves — no AI credits are used and nothing goes live from here. Pick an `action`:
  - create_course (course:{name, depth?, about_html?, why_learn_html?, who_should_learn_html?, tags?}, chapters | modules | subjects, media?: MEDIA): creates a DRAFT course with its structure. Slides inside chapters may be full slide specs (saved now) or {type, title} placeholders (returned in `todo` for add_slide). Returns every chapter id. Optional sessions / levels make one batch per session × level (see courses(action='schema') → batches; reuse existing ones from courses(action='sessions_levels')); leave them out for a simple course. The admin creating it is set as the course's instructor.
    copy_from:{course_id, batch_id?} starts the new course as an independent copy of an existing course's chapters and slides (ask the admin first whether to start from one — courses(action='list')); the copy keeps the source's depth, chapters/modules/subjects you also send are added after the copied ones, and you extend it afterwards with add_chapter / add_slide. A source with several batches needs batch_id (the error lists them).
  - add_chapter (course_id, name, module_id?): another chapter (depth ≥ 3).
  - add_slide (chapter_id, slide:{type, title, …}, position?, status?): one slide.
  - update_slide (slide_id, slide:{type, title, …}, status?): replace a slide of a DRAFT course (same type).
  - reorder (chapter_id, slide_ids:[…]): the chapter's full slide order.
  - import_image (url): public https image → institute media URL to use in document HTML.
  - import_pdf (url): public https PDF → file_id for a {type:'pdf'} slide.
  - discard_slide (slide_id): remove a slide of a DRAFT course.
  - publish_slides (course_id, chapter_id?, slide_ids?): publish DRAFT slides (all of the course, one chapter, or the listed ones) — ONLY when the admin asks to publish / approve slides. On a live course learners see them immediately, so confirm with the admin first.
  Slide STATUS (create_course slides, add_slide, update_slide): status='PUBLISHED' only when the admin asks for the slide to be published / public; status='DRAFT' when they ask for a draft; when they say nothing, it is saved as DRAFT. A status may be set per slide (slide.status) or for the call (status).
  - submit_for_review (course_id): send the finished course for approval; an admin approves it in the dashboard and it goes live. Run courses(action='review') first.
  - update_media (course_id, media: MEDIA): set or replace the course's images — works on live courses too. The course's invite links get the new images wherever they have none of their own.
  MEDIA = {preview? (the catalogue card image), banner? (top of the course page), intro? (image or YouTube video on the course page)}, each {image_url} (public https image — imported into the institute's media) | {file_id} (existing media file) | 'none'; intro also takes {youtube: url}. Never invent image URLs — use ones the admin gives or the institute's media.
  Slide types: document {html}, video {url (YouTube)}, pdf {file_id}, quiz {questions, settings?}, question {question}, assignment {instructions_html, …}.
  ```
</Accordion>

## `course_invites_edit`

**Courses: invite links & payment plans** · Makes changes · Changes live data — the change applies straight away.

Let the connected AI app create invite links and payment plans for courses, and edit an invite's page (name, dates, text, images, where learners land after enrolling) and its registration form (add your existing fields, remove, reorder, make required). Invite edits apply right away, also on live courses. Payment plans are never edited or removed.

<Note>
  **What keeps it safe:** creates invites and payment plans (re-pointing / default only on non-live courses); edits an invite's page and form in place — the admin's explicit choice, live courses included — but only binds existing fields and never changes a field's shared label / type / options or the invite's payment links
</Note>

| Action | What it does |
| :- | :- |
| `create_invite` | |
| `create_payment_plan` | a new payment plan. With invite\_id + course\_id it replaces that invite's plan (only while the course is not live). |
| `make_default` | the course's main enrolment link (only while the course is not live). Every new course already has a default invite at the institute's default price — make your new invite the default or the catalogue keeps using that one. |
| `update_invite` | |
| `update_form_fields` | |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `create_invite`, `create_payment_plan`, `make_default`, `update_invite`, `update_form_fields`.
  </ParamField>

  <ParamField path="course_id" type="string">
    —
  </ParamField>

  <ParamField path="invite_id" type="string">
    —
  </ParamField>

  <ParamField path="payment" type="object">
    PAYMENT (see description).
  </ParamField>

  <ParamField path="payment_option_id" type="string">
    create\_invite: reuse an existing plan from courses(action='payment\_setup').
  </ParamField>

  <ParamField path="name" type="string">
    create\_invite / update\_invite: invite name (create default: course name).
  </ParamField>

  <ParamField path="start_date" type="string">
    YYYY-MM-DD (create default: today).
  </ParamField>

  <ParamField path="end_date" type="string">
    YYYY-MM-DD, optional (link closes after it).
  </ParamField>

  <ParamField path="access_days" type="integer">
    Days of access after enrolling (FREE invites).
  </ParamField>

  <ParamField path="landing" type="object">
    —
  </ParamField>

  <ParamField path="form_fields" type="object[]">
    —
  </ParamField>

  <ParamField path="make_default" type="boolean">
    create\_invite: also make it the default.
  </ParamField>

  <ParamField path="clear_end_date" type="boolean">
    update\_invite: remove the end date.
  </ParamField>

  <ParamField path="images" type="object">
    update\_invite: \{preview?, banner?, media?}.
  </ParamField>

  <ParamField path="redirect_path" type="string">
    update\_invite: learner-app path, e.g. /study-library/courses.
  </ParamField>

  <ParamField path="success_page" type="object">
    update\_invite: \{content?, show\_login\_button?}.
  </ParamField>

  <ParamField path="add" type="object[]">
    update\_form\_fields: \[\{field\_id, required?}].
  </ParamField>

  <ParamField path="remove" type="string[]">
    update\_form\_fields.
  </ParamField>

  <ParamField path="required" type="object">
    update\_form\_fields: \{field\_id: bool}.
  </ParamField>

  <ParamField path="order" type="string[]">
    update\_form\_fields.
  </ParamField>

  <ParamField path="batch_ids" type="string[]">
    Which batches (session × level, ids from courses(action='get')) the invite enrols into. Needed when the course has several; several make one bundled invite. make\_default: limit to these batches.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Create invite links (the page learners enrol / pay through) and payment plans for a course. Never edits or deletes anything. Read courses(action='invites') and courses(action='payment_setup') first; never invent prices — ask the admin. Pick an `action`:
  - create_invite (course_id, payment: PAYMENT | payment_option_id, batch_ids? (required when the course has several batches — ask which session / level), name?, start_date?, end_date?, access_days?, landing?: {description_html, learning_outcome_html, about_html, target_audience_html, tags}, form_fields?: [{label, type: text|number|dropdown, required?, options?}], make_default?): a new invite. The registration form always starts with the institute's default fields (name, email, phone…); form_fields adds to them (a label the institute already has reuses that field as it is). Landing copy defaults to the course's own. Returns the shareable link.
  - create_payment_plan (payment: PAYMENT, invite_id?, course_id?): a new payment plan. With invite_id + course_id it replaces that invite's plan (only while the course is not live).
  - make_default (course_id, invite_id): the course's main enrolment link (only while the course is not live). Every new course already has a default invite at the institute's default price — make your new invite the default or the catalogue keeps using that one.
  - update_invite (course_id, invite_id, + any of: name, start_date, end_date, clear_end_date, landing (as above — only the keys you pass change), images: {preview?, banner?, media?} each {image_url} (public https, imported) | {file_id} | 'course' (the course's own) | 'none' — media also takes {youtube: url}, redirect_path (where learners land after enrolling: a learner-app path like /study-library/courses — ask the admin for it; '' resets to the default), success_page: {content?, show_login_button?}): edits the invite's page. Payment plan and form are untouched. Allowed on live courses — new visitors see the change at once.
  - update_form_fields (course_id, invite_id, + any of: add: [{field_id, required?}] (only the institute's existing fields — courses(action='get_invite') lists them as available_fields), remove: [field_id], required: {field_id: true|false}, order: [field_id, …] (unlisted fields keep their order after these)): this invite's registration form. A label works wherever a field_id does. Never creates, renames or changes the type / options of a field (every form using it shares that); Full Name, Email and Phone cannot be removed. Allowed on live courses.
  PAYMENT = {type: FREE | ONE_TIME | SUBSCRIPTION, name?, currency (ISO, e.g. INR / USD — required unless FREE), require_approval?: bool (default false: learners get access as soon as they pay/register),
    ONE_TIME: price, strike_price? (shown struck through), validity_days? (omit = lifetime access)
    SUBSCRIPTION: plans: [{name, price, strike_price?, validity_days (e.g. 30, 90, 365)}] (1-6 plans)
    FREE: validity_days? (omit = unlimited)}
  ```
</Accordion>

## `course_structure_edit`

**Courses: change structure / depth** · Makes changes · Changes live data — the change applies straight away. · Turned on separately (opt-in)

Let the connected AI app change how many levels a course has (subjects / modules / chapters) or regroup its content — for example turn a chapters-only course into modules of chapters, with groups it proposes from the content and you adjust. It always shows you the new structure first and changes it only after you confirm. Works on live courses too: learners see the new structure at once and keep their slide progress. Nothing is copied or deleted.

<Note>
  **What keeps it safe:** LIVE, confirm-gated, the admin's explicit choice: changes a course's depth / grouping in ONE admin-core transaction for every batch; nothing is copied or deleted — slides and kept containers keep their ids, emptied containers are soft-deleted; refuses content shared with another course and trees that drop or duplicate a slide; every write needs the confirm\_token of a dry-run preview of the identical request
</Note>

| Action | What it does |
| :- | :- |
| `preview` | checks the change and shows the result — the new tree, what is created / renamed / removed, slides moved, release (drip) rules re-pointed or dropped, and how many learners see it at once on a live course. Nothing is saved. Returns a confirm\_token (15 min). |
| `apply` | does it, after the admin said yes to the preview. |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `preview`, `apply`.
  </ParamField>

  <ParamField path="course_id" type="string" required>
    —
  </ParamField>

  <ParamField path="target_depth" type="integer" required>
    One of: `2`, `3`, `4`, `5`.
  </ParamField>

  <ParamField path="outline" type="object">
    The new grouping (see description).
  </ParamField>

  <ParamField path="confirm_token" type="string">
    apply: the token preview returned.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Change how many levels a course has, or regroup its content. Depth 5 = subjects → modules → chapters → slides, 4 = modules → chapters → slides, 3 = chapters → slides, 2 = slides only. Nothing is copied or deleted: slides keep their ids (learner progress survives); only the grouping changes, for every batch. Read courses(action='get') first for the current tree and ids. Pick an `action`:
  - preview (course_id, target_depth, outline?): checks the change and shows the result — the new tree, what is created / renamed / removed, slides moved, release (drip) rules re-pointed or dropped, and how many learners see it at once on a live course. Nothing is saved. Returns a confirm_token (15 min).
  - apply (same arguments + confirm_token): does it, after the admin said yes to the preview.
  Going DOWN a level needs no outline: the level being hidden is merged in its current order (e.g. 3→2 puts every chapter's slides into one list; 4→3 lists every module's chapters). Going UP needs one: propose a grouping from the content (chapter / slide titles), show it to the admin, change it as they ask, then preview it. You may also pass an outline at the same depth to regroup or rename.
  outline, by target depth (existing items by id — keep their id when keeping or renaming them; new containers have only a name; slides always by id, each exactly once):
    5: {subjects: [{id?, name, modules: [{id?, name, chapters: [{id?, name, slide_ids: [..]}]}]}]}
    4: {modules: [{id?, name, chapters: [{id?, name, slide_ids}]}]}
    3: {chapters: [{id?, name, slide_ids}]}
    2: {slide_ids: [..]} (the order learners see)
  Content shared with another course cannot be regrouped here.
  ```
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.