> ## 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.

# Website

> Pages, sections, theme, SEO and site traffic.

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.

## `website`

**Website: view** · Read only

See the institute's websites: pages and what each section shows, traffic, lead-capture health, pre-publish checks, and the interview an AI runs before building a site.

| Action | What it does |
| :- | :- |
| `list` | every site with status, live URL, whether a draft is pending, last published. |
| `get_page` | a page's sections in order (position), each with a label, heading, what it LOOKS like (band colour, layout, images, buttons) and where its data comes from. Use it to match what an admin points at in a screenshot to a section id. |
| `find_section` | where a piece of text appears — section id, position and the exact prop path to patch ('the button that says Book a demo'). |
| `context` | what may be linked on a site — real courses, product pages, lead campaigns (with leads received), folder libraries, the site's theme. Use these ids; never invent them. With library\_id: that library's folders (ids for website\_edit bind\_data data\_kind='folder'). detail=true adds each course's tags, detected language and format, catalogue flag, default invite and payment vendor, the folder libraries (streams → categories → paths) and each product page's steps. |
| `analytics` | views, visitors, sessions, leads, top pages and sources. |
| `lead_summary` | every enquiry form / popup on the site, which campaign it feeds, leads received, and forms wired to nothing. |
| `audit` | the dashboard's pre-publish checks — what is broken or missing before publishing. |
| `review` | design-quality score (0–100, bar 85) with ranked issues and concrete fixes. Iterate with website\_edit(update\_page) until it passes in the mode the page was created in; do it before telling the admin a page is ready. A page saved with design\_source (built from a Figma / design) is reviewed in FIDELITY mode: the design wins, so taste rules (section count, separate hero, proof, closing CTA, bands) are skipped and only defects a visitor would see count. fidelity=true asks for that mode explicitly. |
| `brief_checklist` | the interview to run BEFORE composing a website or page — what to ask (colours, logo, photos, tone, pages, courses, enquiries), what is already known, and the available presets/fonts/design languages. With design\_source (a Figma / design link, or \{kind:'screenshot'\|'url'}) the design answers the look, so it asks only the data questions. |
| `schema` | the component contract you compose pages in — the block types with what each does, design rules, the archetype for a page type, and full example props for the section\_types you name, the header/footer contract (`chrome`, written with set\_layout), the opt-in site-settings contract and an index of design patterns. Read it before website\_edit(create\_page). |
| `patterns` | design patterns — named opt-in looks (catalogue hero, icon stream tabs, editorial sidebar/cards, CTA bands, brand footer, palette…) with what each looks like, Figma cues, the data it needs, and its minimal and full JSON. Use it to match a design (Figma frame, screenshot) to components. Replace each text \<placeholder> with your own copy or a list\_media image; leave every id path (`bound`) EMPTY and wire it afterwards (link\_lead\_form, or the admin in the editor). |
| `list_media` | images the admin has uploaded, ranked with hero-worthy landscape photos first — the ONLY images (besides import\_image) a page may use. |
| `preview` | a screenshot of the DRAFT as the learner site renders it. Look at it before and after edits. Matching a design: pass width (320–1920; a Figma desktop frame is 1440), lang ('hi'), sections=true (each top-level section's id and pixel box), click\_text (open a menu or tab first, e.g. 'Knowledge Streams'), or fidelity=true (1440 + sections); a tall page then comes back as several images, top to bottom. |
| `compare` | render the DRAFT and compare it with the design, section by section — similarity (SSIM), height ratio, background colour ΔE, design text missing on the page, design sections with no counterpart, and hints naming the prop to change — plus a side-by-side image. reference = \{asset\_url} (a url from website\_edit(import\_image) or list\_media) or \{asset\_id} (a list\_media file\_id), or \{tiles:\[…]} for an export cut into pieces; add sections:\[\{name, top, height, texts?}] (the design's frame children, in frame units) and frame\_width to pair against the design's own sections. Only this institute's images stored on this platform are read (its imports, its media library, images already on the site); no other address is ever fetched. Fix the top hints with website\_edit(update\_page) and compare again (at most 3 rounds, then 390 wide and each language). |
| `strings` | the site's texts that still have no translation in `locale` (e.g. 'hi') — exactly what the dashboard's Translations panel lists. Translate them yourself and save with website\_edit(action='set\_translations'); never write the translation into the page. |
| `data_inventory` | everything a design's data needs are matched against — courses in detail, the tag vocabulary with counts, level names, folder libraries, product pages with steps, campaigns and configured payment gateways. Read-only. |
| `data_audit` | checks the DATA behind the site's catalogue widgets — courses with no stream / language / format tag, folders no course matches, version groups that mix languages, unknown or inactive product pages and libraries, courses not on the catalogue, authored prices the widgets compute live, paid invites on an unconfigured gateway. Each item has a fix and a dashboard link for the admin; run it before telling the admin a site is ready. |
| `playbook` | call it FIRST when the admin gives a design — the steps to rebuild it as a draft site with this server's actions, the Figma call budget, stop conditions, and a Figma → pattern table (which design cues mean which pattern). |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `list`, `get_page`, `find_section`, `context`, `analytics`, `lead_summary`, `audit`, `review`, `brief_checklist`, `schema`, `patterns`, `list_media`, `preview`, `compare`, `strings`, `data_inventory`, `data_audit`, `playbook`.
  </ParamField>

  <ParamField path="tag_name" type="string">
    Site name (from `list`). Optional when there is a single/default site.
  </ParamField>

  <ParamField path="page_route" type="string">
    Page route within the site, e.g. 'home', 'about', 'admissions'. Defaults to the first page.
  </ParamField>

  <ParamField path="page_type" type="string">
    schema: which page archetype's rules to include. review: judge the page as this type (default: the type it was created as, else what its content says, else by position/route). One of: `homepage`, `courses`, `course-landing`, `about`, `admissions`, `contact`, `catalog`.
  </ParamField>

  <ParamField path="fidelity" type="boolean">
    review: true = the page reproduces a design (taste rules skipped); default: on for pages saved with design\_source. preview: design-matching mode (1440 wide, sections, full height as tiles); on by default for pages created from a design.
  </ParamField>

  <ParamField path="include_copy" type="boolean">
    get\_page only: include each section's text (capped).
  </ParamField>

  <ParamField path="detail" type="boolean">
    context: add course tags / language / format / invites, folder libraries and product-page steps.
  </ParamField>

  <ParamField path="library_id" type="string">
    data\_audit: the folder library to check courses against when the site names none yet. context: also list this folder library's folders (id from folder\_libraries).
  </ParamField>

  <ParamField path="days" type="integer">
    analytics / lead\_summary: window in days (7, 30 or 90). Default 30.
  </ParamField>

  <ParamField path="section_types" type="string[]">
    schema: block types to return full example props for (e.g. \['heroSection','featureGrid']).
  </ParamField>

  <ParamField path="query" type="string">
    find\_section: the text to look for (case-insensitive). patterns: words to match against pattern ids, looks and Figma cues.
  </ParamField>

  <ParamField path="ids" type="string[]">
    patterns: pattern ids to return in full (e.g. \['catalog.hero','cta.band']); up to 20 per call.
  </ParamField>

  <ParamField path="component" type="string">
    patterns: only patterns of this block type (courseCatalog, ctaBanner, header, footer, globalSettings…).
  </ParamField>

  <ParamField path="section_id" type="string">
    preview: screenshot only this section.
  </ParamField>

  <ParamField path="viewport" type="string">
    preview: default desktop (1280px); mobile is 390px. One of: `desktop`, `mobile`.
  </ParamField>

  <ParamField path="width" type="integer">
    preview / compare: layout width in px (compare and fidelity default 1440).
  </ParamField>

  <ParamField path="lang" type="string">
    preview / compare: render in this site language (a code from the site's languages, e.g. 'hi').
  </ParamField>

  <ParamField path="sections" type="boolean">
    preview: also return each top-level section's id and box (top, height in px).
  </ParamField>

  <ParamField path="click_text" type="string">
    preview: click the element with this text first (opens a mega menu, a tab).
  </ParamField>

  <ParamField path="max_height" type="integer">
    preview: tallest capture in px (default and cap 16000).
  </ParamField>

  <ParamField path="reference" type="object">
    compare: the design image — \{asset\_url} or \{asset\_id}, or \{tiles:\[\{asset\_url|asset\_id}, …]} top to bottom; optional sections, frame\_width, texts.
  </ParamField>

  <ParamField path="kind" type="string">
    list\_media: 'logo', 'photo' or 'any'.
  </ParamField>

  <ParamField path="limit" type="integer">
    list\_media: max items (default 24). strings: max texts (default 200, max 500).
  </ParamField>

  <ParamField path="locale" type="string">
    strings: the language to check, e.g. 'hi' (not the site's base language).
  </ParamField>

  <ParamField path="offset" type="integer">
    strings: skip this many untranslated texts (paging).
  </ParamField>

  <ParamField path="source" type="string">
    playbook: what the design is — a Figma file, screenshots, or an existing website. One of: `figma`, `screenshot`, `url`.
  </ParamField>

  <ParamField path="design_source" type="object">
    brief\_checklist: the design the site is rebuilt from, e.g. \{url:'[https://www.figma.com/design/…'\&#125](https://www.figma.com/design/…'\&#125); — \{url} alone is enough, or \{kind:'screenshot'}. The interview then asks only data questions. Never put an access token here.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Read the institute's websites (the sites built in Manage Pages and served on the learner portal). One tool, pick an `action`:
  - list: every site with status, live URL, whether a draft is pending, last published.
  - get_page (tag_name, page_route, include_copy?): a page's sections in order (position), each with a label, heading, what it LOOKS like (band colour, layout, images, buttons) and where its data comes from. Use it to match what an admin points at in a screenshot to a section id.
  - find_section (tag_name, query, page_route?): where a piece of text appears — section id, position and the exact prop path to patch ('the button that says Book a demo').
  - review (tag_name, page_route?, page_type?, fidelity?): design-quality score (0–100, bar 85) with ranked issues and concrete fixes. Iterate with website_edit(update_page) until it passes in the mode the page was created in; do it before telling the admin a page is ready. A page saved with design_source (built from a Figma / design) is reviewed in FIDELITY mode: the design wins, so taste rules (section count, separate hero, proof, closing CTA, bands) are skipped and only defects a visitor would see count. fidelity=true asks for that mode explicitly.
  - preview (tag_name, page_route?, section_id?, viewport?): a screenshot of the DRAFT as the learner site renders it. Look at it before and after edits. Matching a design: pass width (320–1920; a Figma desktop frame is 1440), lang ('hi'), sections=true (each top-level section's id and pixel box), click_text (open a menu or tab first, e.g. 'Knowledge Streams'), or fidelity=true (1440 + sections); a tall page then comes back as several images, top to bottom.
  - compare (tag_name, page_route?, reference, width?, lang?): render the DRAFT and compare it with the design, section by section — similarity (SSIM), height ratio, background colour ΔE, design text missing on the page, design sections with no counterpart, and hints naming the prop to change — plus a side-by-side image. reference = {asset_url} (a url from website_edit(import_image) or list_media) or {asset_id} (a list_media file_id), or {tiles:[…]} for an export cut into pieces; add sections:[{name, top, height, texts?}] (the design's frame children, in frame units) and frame_width to pair against the design's own sections. Only this institute's images stored on this platform are read (its imports, its media library, images already on the site); no other address is ever fetched. Fix the top hints with website_edit(update_page) and compare again (at most 3 rounds, then 390 wide and each language).
  - context (tag_name?, detail?, library_id?): what may be linked on a site — real courses, product pages, lead campaigns (with leads received), folder libraries, the site's theme. Use these ids; never invent them. With library_id: that library's folders (ids for website_edit bind_data data_kind='folder'). detail=true adds each course's tags, detected language and format, catalogue flag, default invite and payment vendor, the folder libraries (streams → categories → paths) and each product page's steps.
  - data_inventory (tag_name?): everything a design's data needs are matched against — courses in detail, the tag vocabulary with counts, level names, folder libraries, product pages with steps, campaigns and configured payment gateways. Read-only.
  - data_audit (tag_name?, library_id?): checks the DATA behind the site's catalogue widgets — courses with no stream / language / format tag, folders no course matches, version groups that mix languages, unknown or inactive product pages and libraries, courses not on the catalogue, authored prices the widgets compute live, paid invites on an unconfigured gateway. Each item has a fix and a dashboard link for the admin; run it before telling the admin a site is ready.
  - analytics (tag_name?, days?): views, visitors, sessions, leads, top pages and sources.
  - lead_summary (tag_name, days?): every enquiry form / popup on the site, which campaign it feeds, leads received, and forms wired to nothing.
  - audit (tag_name, page_route?): the dashboard's pre-publish checks — what is broken or missing before publishing.
  - brief_checklist (tag_name?, design_source?): the interview to run BEFORE composing a website or page — what to ask (colours, logo, photos, tone, pages, courses, enquiries), what is already known, and the available presets/fonts/design languages. With design_source (a Figma / design link, or {kind:'screenshot'|'url'}) the design answers the look, so it asks only the data questions.
  - playbook (source: figma|screenshot|url): call it FIRST when the admin gives a design — the steps to rebuild it as a draft site with this server's actions, the Figma call budget, stop conditions, and a Figma → pattern table (which design cues mean which pattern).
  - schema (page_type?, section_types?): the component contract you compose pages in — the block types with what each does, design rules, the archetype for a page type, and full example props for the section_types you name, the header/footer contract (`chrome`, written with set_layout), the opt-in site-settings contract and an index of design patterns. Read it before website_edit(create_page).
  - patterns (ids?, component?, query?): design patterns — named opt-in looks (catalogue hero, icon stream tabs, editorial sidebar/cards, CTA bands, brand footer, palette…) with what each looks like, Figma cues, the data it needs, and its minimal and full JSON. Use it to match a design (Figma frame, screenshot) to components. Replace each text <placeholder> with your own copy or a list_media image; leave every id path (`bound`) EMPTY and wire it afterwards (link_lead_form, or the admin in the editor).
  - list_media (kind?, limit?): images the admin has uploaded, ranked with hero-worthy landscape photos first — the ONLY images (besides import_image) a page may use.
  - strings (tag_name, locale, offset?, limit?): the site's texts that still have no translation in `locale` (e.g. 'hi') — exactly what the dashboard's Translations panel lists. Translate them yourself and save with website_edit(action='set_translations'); never write the translation into the page.
  tag_name is the site's name from `list`; when the institute has one site it may be omitted.
  ```
</Accordion>

## `catalog_data_edit`

**Website: set up course data** · Makes changes · Adds to live records — what it adds is visible straight away. · Turned on separately (opt-in)

Let the connected AI app add the course data a website design needs: create folder libraries and folders (new folders start hidden), append tags to courses (existing tags are kept), create product pages as DRAFT and add catalogue courses to a store page. It shows each change first and never deletes, renames or hides anything; activating a product page and payment settings stay with you. These are live records, not drafts: tags added to live courses change the site's filters at once, and courses added to an active store page are on sale at once. Uses no AI credits.

<Note>
  **What keeps it safe:** additive only; never deletes or renames existing records; new folder nodes start HIDDEN; tags are appended; product pages are created DRAFT. Every action is a dry run first: applying needs the dry run's plan\_token (10 minutes, refused when the data changed); a shown folder changes only while no live site uses its library; the store sync never switches a course off; activating a product page and invite payment vendors stay admin clicks
</Note>

| Action | What it does |
| :- | :- |
| `create_folder_library` | a new empty folder library (not used by any site until you bind it with website\_edit(bind\_data)). |
| `upsert_folder_nodes` | folders matched by key. New folders are created HIDDEN (the admin shows them in Manage Pages → Folders). A matching folder gets the fields you send that differ — never its title, parent or visibility — and only while the folder is HIDDEN or no live site uses the library. Folder fields: key (its URL key / slug — the match key), parent\_key?, title, subtitle?, tagline?, description?, cta? (button label), link\_url? ('/route' or https\://…), course\_tag? (the course tag the folder filters by; default = key), image\_url? (an institute asset: website(list\_media) / website\_edit(import\_image)), accent\_color? (#hex), coming\_soon?, audience\_id? (an ACTIVE lead campaign of this institute). |
| `add_course_tags` | appends tags (lower-case, \[a-z0-9-], e.g. 'format-ebook', 'for-students'); existing tags are kept. Returns before/after per course. On a live course the new tags change the site's filters at once. |
| `create_product_page` | a DRAFT product page selling each course through its DEFAULT invite (or invite\_id), its payment option and cheapest active plan. 'path' = one batch per step (pass package\_session\_id when a course has several); 'store' = every sellable batch. It is listed nowhere until a site binds it and the admin activates it, but its link already opens for anyone who has the code — do not share it before then. |
| `sync_store` | adds the catalogue courses the product page does not sell yet (never switches one off) and returns why any were skipped. On an ACTIVE page the added courses are on sale at once. |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `create_folder_library`, `upsert_folder_nodes`, `add_course_tags`, `create_product_page`, `sync_store`.
  </ParamField>

  <ParamField path="dry_run" type="boolean">
    Default true: report the change without making it. false = apply (needs plan\_token).
  </ParamField>

  <ParamField path="plan_token" type="string">
    dry\_run=false: the plan\_token the dry run of these same arguments returned (valid 10 minutes). It is this server's own check value, not a credential.
  </ParamField>

  <ParamField path="name" type="string">
    create\_folder\_library / create\_product\_page: its name.
  </ParamField>

  <ParamField path="description" type="string">
    create\_folder\_library: optional description.
  </ParamField>

  <ParamField path="library_id" type="string">
    upsert\_folder\_nodes: the library (website(data\_inventory) folder\_libraries).
  </ParamField>

  <ParamField path="nodes" type="object[]">
    upsert\_folder\_nodes: the folders.
  </ParamField>

  <ParamField path="assignments" type="object[]">
    add\_course\_tags: tags to append per course.
  </ParamField>

  <ParamField path="items" type="object[]">
    create\_product\_page: the courses, in display order.
  </ParamField>

  <ParamField path="role" type="string">
    create\_product\_page: default 'path'. One of: `path`, `store`.
  </ParamField>

  <ParamField path="product_page_code" type="string">
    sync\_store: the product page (website(data\_inventory) product\_pages code).
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Set up the catalogue DATA a site design needs — ADDITIVE ONLY: never deletes, renames, hides or clears anything. Every action is a DRY RUN by default and returns exactly what it would change plus a plan_token: show the plan to the admin, then call again with the SAME arguments, dry_run=false and that plan_token (valid 10 minutes; refused when the data changed since). Unknown arguments are refused. Ids come from website(action='data_inventory').
  - create_folder_library (name, description?): a new empty folder library (not used by any site until you bind it with website_edit(bind_data)).
  - upsert_folder_nodes (library_id, nodes[≤100]): folders matched by key. New folders are created HIDDEN (the admin shows them in Manage Pages → Folders). A matching folder gets the fields you send that differ — never its title, parent or visibility — and only while the folder is HIDDEN or no live site uses the library. Folder fields: key (its URL key / slug — the match key), parent_key?, title, subtitle?, tagline?, description?, cta? (button label), link_url? ('/route' or https://…), course_tag? (the course tag the folder filters by; default = key), image_url? (an institute asset: website(list_media) / website_edit(import_image)), accent_color? (#hex), coming_soon?, audience_id? (an ACTIVE lead campaign of this institute).
  - add_course_tags (assignments[≤50] of {course_id, add[]}): appends tags (lower-case, [a-z0-9-], e.g. 'format-ebook', 'for-students'); existing tags are kept. Returns before/after per course. On a live course the new tags change the site's filters at once.
  - create_product_page (name, items[{course_id, invite_id?, package_session_id?}], role='path'|'store'): a DRAFT product page selling each course through its DEFAULT invite (or invite_id), its payment option and cheapest active plan. 'path' = one batch per step (pass package_session_id when a course has several); 'store' = every sellable batch. It is listed nowhere until a site binds it and the admin activates it, but its link already opens for anyone who has the code — do not share it before then.
  - sync_store (product_page_code): adds the catalogue courses the product page does not sell yet (never switches one off) and returns why any were skipped. On an ACTIVE page the added courses are on sale at once.
  Never send access tokens or other credentials.
  ```
</Accordion>

## `design_import`

**Website: import Figma designs** · Makes changes · Drafts only — nothing goes live until someone publishes it in the admin portal.

Let the connected AI app turn a Figma design it read with its own Figma tools into a site plan (colours, fonts, sections, the data it needs) and save it as a DRAFT site. The design files it sends are kept 24 hours for this institute only. Saving also needs 'Build and edit pages'. Uses no AI credits.

<Note>
  **What keeps it safe:** draft-only: plan parses the Figma results the caller sends and stores them 24 h for this institute (its own table, never served elsewhere); save\_draft saves a DRAFT through website\_edit's create\_site / create\_page and also needs website\_edit's group
</Note>

| Action | What it does |
| :- | :- |
| `plan` | Figma calls are scarce (Starter/View seats get about 6 a month), so spend them like this: ONE get\_metadata on the page, ONE get\_design\_context per top-level frame (if a frame's code comes back cut off, call get\_design\_context on its child frames after the cut), get\_variable\_defs only if the file has variables. Metadata alone has no colours or fonts: send the design code too. Send ONE Figma result per call — you re-type every argument, and a page's results (\~100k characters each) do not fit one reply: first metadata\_xml alone with upload\_only=true (it returns an import\_id), then each frame's design\_code in its own call with that import\_id and upload\_only=true, and the LAST frame's call without upload\_only to get the plan. The upload is kept 24 h for this institute only. |
| `save_draft` | save the plan — or your corrected site\_json (\{theme?, pages?, header?, footer?}) — as a DRAFT through website\_edit's create\_site / create\_page, each page marked with its Figma frame (review then runs in fidelity mode). Needs the 'Website: edit drafts' capability. Nothing goes live. |

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

  <ParamField path="source" type="string">
    plan: 'client' = you send the Figma results (the only supported source). One of: `client`, `figma_url`.
  </ParamField>

  <ParamField path="url" type="string">
    The figma.com link of the design (kept as the pages' design source).
  </ParamField>

  <ParamField path="metadata_xml" type="string">
    plan: get\_metadata output exactly as returned.
  </ParamField>

  <ParamField path="design_code" type="object[]">
    plan: get\_design\_context output per frame.
  </ParamField>

  <ParamField path="variables" type="object | string">
    plan: get\_variable\_defs output (optional).
  </ParamField>

  <ParamField path="frame_ids" type="string[]">
    plan: only these top-level frames (default: every frame).
  </ParamField>

  <ParamField path="import_id" type="string">
    plan: add to an earlier upload; save\_draft: the plan to save.
  </ParamField>

  <ParamField path="upload_only" type="boolean">
    plan: store this part and return without planning.
  </ParamField>

  <ParamField path="new_site_name" type="string">
    save\_draft: create a NEW draft site with this name.
  </ParamField>

  <ParamField path="tag_name" type="string">
    save\_draft: add the pages to this existing site (as a draft).
  </ParamField>

  <ParamField path="pages" type="string[]">
    save\_draft: only these page routes from the plan.
  </ParamField>

  <ParamField path="site_json" type="object">
    save\_draft: your corrected draft — \{theme?, pages?, header?, footer?} in the shape of the plan's site\_json\_draft. Keys you leave out come from the plan.
  </ParamField>

  <ParamField path="apply_theme" type="boolean">
    save\_draft with tag\_name: also set the design's theme on that site's draft (default false; a new site always gets it).
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Turn a Figma design YOU read with your own Figma tools into a Vacademy site plan: exact palette, fonts and content width, each frame cut into sections matched to the site's design patterns, the data the design needs (folder library, tags, product pages, campaigns, formats, languages), its image assets and a site JSON draft. Nothing is fetched from Figma here and no model runs.
  - plan (source='client', metadata_xml, design_code[], variables?, frame_ids?, url?, import_id?, upload_only?): Figma calls are scarce (Starter/View seats get about 6 a month), so spend them like this: ONE get_metadata on the page, ONE get_design_context per top-level frame (if a frame's code comes back cut off, call get_design_context on its child frames after the cut), get_variable_defs only if the file has variables. Metadata alone has no colours or fonts: send the design code too. Send ONE Figma result per call — you re-type every argument, and a page's results (~100k characters each) do not fit one reply: first metadata_xml alone with upload_only=true (it returns an import_id), then each frame's design_code in its own call with that import_id and upload_only=true, and the LAST frame's call without upload_only to get the plan. The upload is kept 24 h for this institute only.
  - save_draft (import_id, new_site_name | tag_name, pages?, site_json?, url?, apply_theme?): save the plan — or your corrected site_json ({theme?, pages?, header?, footer?}) — as a DRAFT through website_edit's create_site / create_page, each page marked with its Figma frame (review then runs in fidelity mode). Needs the 'Website: edit drafts' capability. Nothing goes live.
  source='figma_url' is not supported (this server holds no Figma credentials). Never send access tokens.
  ```
</Accordion>

## `website_edit`

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

Let the connected AI app build and change websites — compose pages, edit sections, set colours and fonts, course formats and languages, translations, wire forms to lead campaigns and sections to folder libraries or product pages. Every change is saved as a draft; nothing goes live until you publish it in Manage Pages. Uses no AI credits.

<Note>
  **What keeps it safe:** draft-only: every action saves a draft revision; discard\_draft undoes it; request\_publish only reads (checks + diff vs live + editor link) — publishing is an admin click
</Note>

| Action | What it does |
| :- | :- |
| `create_page` | validate, audit and save a page you composed. Returns issues to fix (resubmit with update\_page) and the editor\_url. Pass design\_source when the page reproduces a design (Figma, screenshot): review then judges it in fidelity mode. |
| `create_site` | a whole NEW draft site. A page that is its own frame can carry pages\[].design\_source. |
| `add_html_page` | a STATIC page you write as HTML + CSS (a full document or just the body) — for pages the block types cannot express, or when the admin hands you HTML. Scripts are removed; \<style> moves into css; images must be institute media; links become site hooks. See website(action='schema').html\_page\_contract. |
| `update_page` | insert / update / remove / move sections you author. design\_source sets (or \{clear: true} removes) the design an existing page reproduces. |
| `set_layout` | the site's header and footer components. |
| `add_section` | insert one block with default content. |
| `set_theme` | colours, fonts, radius, atmosphere, motion, and for a design (Figma) the exact named palette and the content width. |
| `set_site_settings` | sticky header, back-to-top, density, audience, site-wide lead popup, site-level SEO (keywords, organization). |
| `set_courses` | which courses a course block shows. |
| `link_lead_form` | send a form or popup button's enquiries to a lead campaign. Ids ONLY from website(action='context') / audience\_forms. section\_id 'header' / 'footer' (no page\_route) targets the site chrome, e.g. the footer newsletter. surface\_path (from website(lead\_summary)) wires just that one form when a section has several. |
| `set_seo` | |
| `import_image` | |
| `discard_draft` | throw the draft away — the undo for everything above. |
| `set_catalog_settings` | course formats (label + the level names / tags that mean each), their order, course languages + version groups (the same course in two languages as ONE card; course ids from website(action='context')), naming (e.g. the Level filter called 'Format'), and the site-wide cart's store product page. |
| `set_translations` | merge translations into the site's dictionary for `locale` — \{exact source text from website(action='strings'): translation}; null removes one; a translation equal to its source keeps the text as is. enable=true offers the language on the site. |
| `bind_data` | bind live data instead of removing a section the audit flags as unbound — a folder library (folderBrowser, learningPath list, courseCatalog stream tabs, header mega menu), a folder (folderBrowser start folder, learningPath folder) or a product page code (learningPath, productPageOffer). Ids are checked against the institute's own data: library ids from website(action='context') folder\_libraries, folder ids from website(action='context', library\_id=…), product page codes from website(action='context'). Without page\_route the section is found on whichever page has it. |
| `request_publish` | the hand-over check when you think the draft is done. Runs the pre-publish checks, the data audit and the review (fidelity mode for pages from a design) and returns ready + blockers, what publishing would change on the live site (diff\_vs\_live; first\_publish for a site never published), a stale-draft verdict and editor\_url. It NEVER publishes: the admin presses Publish in the editor (or, when the website\_publish tool is enabled and the draft is ready, publish\_confirm carries the single-use token website\_publish needs after the admin says yes). |

<Accordion title="Parameters">
  <ParamField path="action" type="string" required>
    The action to run: `create_page`, `create_site`, `add_html_page`, `update_page`, `set_layout`, `add_section`, `set_theme`, `set_site_settings`, `set_courses`, `link_lead_form`, `set_seo`, `import_image`, `discard_draft`, `set_catalog_settings`, `set_translations`, `bind_data`, `request_publish`.
  </ParamField>

  <ParamField path="tag_name" type="string">
    Existing site (from website list).
  </ParamField>

  <ParamField path="new_site_name" type="string">
    create\_page / create\_site: name for a NEW site; becomes its URL path.
  </ParamField>

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

  <ParamField path="section_id" type="string">
    A section id from website(get\_page).
  </ParamField>

  <ParamField path="page" type="object">
    A page you composed. Get the component contract from website(action='schema') first.
  </ParamField>

  <ParamField path="pages" type="object[]">
    create\_site: the pages, in navigation order.
  </ParamField>

  <ParamField path="page_type" type="string">
    create\_page: which archetype the page follows (drives the audit). One of: `homepage`, `courses`, `course-landing`, `about`, `admissions`, `contact`, `catalog`.
  </ParamField>

  <ParamField path="design_source" type="object">
    The design a page reproduces (a Figma frame, a screenshot, a site): create\_page / create\_site (site-wide, or per page inside `pages[]` when each page is its own frame), update\_page (set it on an existing page, or \{clear: true} to go back to standard review). Review then runs in FIDELITY mode — the design wins over the generic quality rules. Only kind, node and frame name are saved on the page (the published site is public): the link and file key are not stored. Never put an access token here.
  </ParamField>

  <ParamField path="surface_path" type="string">
    link\_lead\_form: one form's prop path from website(lead\_summary), e.g. 'props.newsletter.audienceId'.
  </ParamField>

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

  <ParamField path="header" type="object">
    One section, per website(action='schema'): \{id (kebab-case, unique), type, enabled: true, props, style?}.
  </ParamField>

  <ParamField path="footer" type="object">
    One section, per website(action='schema'): \{id (kebab-case, unique), type, enabled: true, props, style?}.
  </ParamField>

  <ParamField path="route" type="string">
    add\_html\_page: URL path segment for the new page.
  </ParamField>

  <ParamField path="title" type="string">
    add\_html\_page: page title (defaults to the HTML \<title>/\<h1>).
  </ParamField>

  <ParamField path="html" type="string">
    add\_html\_page: the page's HTML — a full document or the body markup. Max 200 KB.
  </ParamField>

  <ParamField path="css" type="string">
    add\_html\_page: extra stylesheet (in addition to any \<style> in html). Max 150 KB.
  </ParamField>

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

  <ParamField path="hide_site_chrome" type="boolean">
    add\_html\_page: hide the site header/footer on this page (default true — a pasted page usually brings its own).
  </ParamField>

  <ParamField path="replace_existing" type="boolean">
    add\_html\_page: overwrite a page that already has this route (only if it is an HTML page).
  </ParamField>

  <ParamField path="theme" type="object">
    Colours and type. Omit a field to leave it unchanged.
  </ParamField>

  <ParamField path="site_settings" type="object">
    Site-wide behaviour and search settings (globalSettings), apart from the theme.
  </ParamField>

  <ParamField path="section_type" type="string">
    Block type, e.g. testimonialSection, faqSection, courseShowcase, leadForm.
  </ParamField>

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

  <ParamField path="props" type="object">
    add\_section: prop overrides for the new block.
  </ParamField>

  <ParamField path="url" type="string">
    import\_image: public https image URL (incl. Figma MCP asset links).
  </ParamField>

  <ParamField path="urls" type="string[]">
    import\_image: several public https image URLs at once (max 16).
  </ParamField>

  <ParamField path="data_base64" type="string">
    import\_image: the image file itself, base64 or a data: URL (max 10 MB) — for local files / design exports.
  </ParamField>

  <ParamField path="file_name" type="string">
    import\_image with data\_base64: the file's name (for the media library).
  </ParamField>

  <ParamField path="kind" type="string">
    One of: `logo`, `photo`, `banner`, `inspiration`.
  </ParamField>

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

  <ParamField path="source" type="string">
    One of: `all`, `showcase`, `product_page`.
  </ParamField>

  <ParamField path="mode" type="string">
    set\_courses showcase mode. One of: `newest`, `onSale`, `tag`, `picked`, `comingSoon`.
  </ParamField>

  <ParamField path="tag" type="string">
    set\_courses mode=tag: the course tag.
  </ParamField>

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

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

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

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

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

  <ParamField path="catalog_settings" type="object">
    Catalogue-wide settings (globalSettings). Maps merge key by key (null removes a key); lists replace. Course ids and product page codes ONLY from website(action='context').
  </ParamField>

  <ParamField path="locale" type="string">
    set\_translations: the language, e.g. 'hi'.
  </ParamField>

  <ParamField path="strings" type="object">
    set\_translations: \{exact source text: translation | null}.
  </ParamField>

  <ParamField path="enable" type="boolean">
    set\_translations: true offers the language on the site (switcher on); false turns the switcher off.
  </ParamField>

  <ParamField path="locale_label" type="string">
    set\_translations: what the language switcher shows, e.g. 'हिन्दी'.
  </ParamField>

  <ParamField path="data_kind" type="string">
    bind\_data: what to bind. One of: `folderLibrary`, `folder`, `productPage`.
  </ParamField>

  <ParamField path="data_id" type="string">
    bind\_data: the library id, folder id or product page CODE (from website(action='context')).
  </ParamField>

  <ParamField path="nav_index" type="integer">
    bind\_data on the header: which navigation item (0-based) becomes the mega menu.
  </ParamField>

  <ParamField path="path" type="string">
    bind\_data: an explicit prop path for the id inside an object the section already has, e.g. columnSections\[1].productPageCode; on a featured learning path, featured.code or pathExtras\[i].code (data\_kind='productPage').
  </ParamField>

  <ParamField path="library_id" type="string">
    bind\_data data\_kind=folder: the folder's library when the section has none yet. request\_publish: the library the data audit checks folders in.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Change the institute's websites. EVERY change is saved as a DRAFT the admin reviews and publishes in the dashboard — nothing goes live from here, and nothing here calls a model: YOU compose the content. Before composing, run website(action='brief_checklist') and website(action='schema'). Pick an `action`:
  - create_page (tag_name OR new_site_name, page, page_type?, theme?, design_source?): validate, audit and save a page you composed. Returns issues to fix (resubmit with update_page) and the editor_url. Pass design_source when the page reproduces a design (Figma, screenshot): review then judges it in fidelity mode.
  - create_site (new_site_name, pages, theme?, site_settings?, header?, footer?, design_source?): a whole NEW draft site. A page that is its own frame can carry pages[].design_source.
  - add_html_page (tag_name OR new_site_name, route, html, css?, title?, seo?, hide_site_chrome?, replace_existing?): a STATIC page you write as HTML + CSS (a full document or just the body) — for pages the block types cannot express, or when the admin hands you HTML. Scripts are removed; <style> moves into css; images must be institute media; links become site hooks. See website(action='schema').html_page_contract.
  - update_page (tag_name, page_route, ops, design_source?): insert / update / remove / move sections you author. design_source sets (or {clear: true} removes) the design an existing page reproduces.
  - set_layout (tag_name, header?, footer?): the site's header and footer components.
  - add_section (tag_name, page_route, section_type, after_section_id?, props?): insert one block with default content.
  - set_theme (tag_name, theme): colours, fonts, radius, atmosphere, motion, and for a design (Figma) the exact named palette and the content width.
  - set_catalog_settings (tag_name, catalog_settings): course formats (label + the level names / tags that mean each), their order, course languages + version groups (the same course in two languages as ONE card; course ids from website(action='context')), naming (e.g. the Level filter called 'Format'), and the site-wide cart's store product page.
  - set_translations (tag_name, locale, strings, enable?, locale_label?): merge translations into the site's dictionary for `locale` — {exact source text from website(action='strings'): translation}; null removes one; a translation equal to its source keeps the text as is. enable=true offers the language on the site.
  - bind_data (tag_name, section_id | 'header', data_kind, data_id, page_route?, path?, nav_index?, library_id?): bind live data instead of removing a section the audit flags as unbound — a folder library (folderBrowser, learningPath list, courseCatalog stream tabs, header mega menu), a folder (folderBrowser start folder, learningPath folder) or a product page code (learningPath, productPageOffer). Ids are checked against the institute's own data: library ids from website(action='context') folder_libraries, folder ids from website(action='context', library_id=…), product page codes from website(action='context'). Without page_route the section is found on whichever page has it.
  - set_site_settings (tag_name, site_settings): sticky header, back-to-top, density, audience, site-wide lead popup, site-level SEO (keywords, organization).
  - set_courses (tag_name, page_route, section_id, source: all|showcase|product_page, mode?, course_ids?, limit?, product_page_code?): which courses a course block shows.
  - link_lead_form (tag_name, page_route, section_id, audience_id, surface_path?): send a form or popup button's enquiries to a lead campaign. Ids ONLY from website(action='context') / audience_forms. section_id 'header' / 'footer' (no page_route) targets the site chrome, e.g. the footer newsletter. surface_path (from website(lead_summary)) wires just that one form when a section has several.
  - set_seo (tag_name, page_route, meta_title?, meta_description?).
  - import_image (url | urls (max 16) | data_base64 + file_name, kind, caption?): copy a public https image (up to 25 MB; big ones are downscaled to WebP, 2400 px) or a base64 file (up to 10 MB) into the media library so it can be placed. Returns url + sha256; the same file imported twice returns the copy already hosted. Figma MCP asset links (https://www.figma.com/api/mcp/asset/…) expire in 7 days — import them in the same session. SVGs are sanitised. Images on pages must come from list_media or import_image — never invent URLs.
  - discard_draft (tag_name): throw the draft away — the undo for everything above.
  - request_publish (tag_name, library_id?): the hand-over check when you think the draft is done. Runs the pre-publish checks, the data audit and the review (fidelity mode for pages from a design) and returns ready + blockers, what publishing would change on the live site (diff_vs_live; first_publish for a site never published), a stale-draft verdict and editor_url. It NEVER publishes: the admin presses Publish in the editor (or, when the website_publish tool is enabled and the draft is ready, publish_confirm carries the single-use token website_publish needs after the admin says yes).
  Every result carries editor_url: tell the admin to review and publish there.
  ```
</Accordion>

## `website_publish`

**Website: publish** · Makes changes · Changes live data — the change applies straight away. · Turned on separately (opt-in)

Let the connected AI app publish a website draft, or roll the live site back to an earlier published version, after you say yes in the chat. Changes go live at once. Each publish needs a fresh check of that exact draft from the last 10 minutes; a draft older than the live site, or edited after the check, is refused, and a rollback never overwrites an unpublished draft. Every version stays in the history. Needs 'Build and edit pages' for the check. Turned on separately.

<Note>
  **What keeps it safe:** LIVE, two-step confirmed: publish needs a single-use confirm\_token from website\_edit(request\_publish) less than 10 minutes earlier on the same draft (id + JSON hash) and live revision, and admin-core refuses a draft older than live (409, never overrideStale); rollback republishes an earlier PUBLISHED revision through a new draft + publish with the same token rule and refuses while a draft is open. Nothing is deleted: every version stays in the history
</Note>

| Action | What it does |
| :- | :- |
| `publish` | run website\_edit(action='request\_publish') first; when it is ready it returns publish\_confirm.confirm\_token. Show the admin diff\_vs\_live.summary, ask, and on a clear yes call this with that token. The token works once, for that exact draft: any edit after request\_publish, a newer live version or a draft older than live is refused — re-run request\_publish. Returns published\_revision\_no, previous\_live\_revision\_no and rollback\_hint. |
| `rollback` | bring back an earlier PUBLISHED version (previous\_live\_revision\_no from a publish; without to\_revision\_no the error lists the published versions). First call without confirm\_token: returns diff\_vs\_live and a token. Show the admin, ask, and on a clear yes call again with the same to\_revision\_no and the token. Refused while the site has an unpublished draft. |

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

  <ParamField path="tag_name" type="string">
    The site (from website list); the token must be for it.
  </ParamField>

  <ParamField path="confirm_token" type="string">
    publish: publish\_confirm.confirm\_token from website\_edit(request\_publish). rollback: the token the first rollback call returned. Single use, 10 minutes.
  </ParamField>

  <ParamField path="to_revision_no" type="integer">
    rollback: the published revision number to bring back.
  </ParamField>
</Accordion>

<Accordion title="What your AI app is told about this tool">
  ```text theme={null}
  Put a website draft LIVE, or roll the live site back to an earlier published version. This changes what visitors see at once, so every call needs a confirm_token from a check made less than 10 minutes earlier, and only after the admin has seen what changes and clearly said yes.
  - publish (tag_name, confirm_token): run website_edit(action='request_publish') first; when it is ready it returns publish_confirm.confirm_token. Show the admin diff_vs_live.summary, ask, and on a clear yes call this with that token. The token works once, for that exact draft: any edit after request_publish, a newer live version or a draft older than live is refused — re-run request_publish. Returns published_revision_no, previous_live_revision_no and rollback_hint.
  - rollback (tag_name, to_revision_no, confirm_token?): bring back an earlier PUBLISHED version (previous_live_revision_no from a publish; without to_revision_no the error lists the published versions). First call without confirm_token: returns diff_vs_live and a token. Show the admin, ask, and on a clear yes call again with the same to_revision_no and the token. Refused while the site has an unpublished draft.
  Never pass credentials; confirm_token is the only token this tool takes. Never say the site is live unless this tool returned published=true.
  ```
</Accordion>


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