USE THIS TOOL whenever the user asks for slides, a deck, a presentation, a pitch, or a slideshow, even a small/informal one (e.g. 'make me 3 slides about X').
Create a Miro slide deck from one or more HTML slides. The deck materializes as a slide_container with child slide frames the next time any user opens the board — no Miro tab needs to be open at creation time.
ONE CALL PER DECK: pass EVERY slide of the deck in a single call's input list. There is NO per-call slide limit and no slide count fails on its own — a deck of 3, 10, or 30+ slides is one call. A second call WITHOUT reference_deck_id creates a separate, independent deck; you cannot append to or extend an existing deck, so splitting one deck across several calls fragments it into multiple containers on the board. If a call fails (e.g. transient HTTP 500), fix the offending slide and retry the SAME single call with the full set — NEVER split it into smaller batches, and never infer a slide-count limit from a failure.
Images: leave external http/https URLs in the HTML untouched — the server fetches and uploads them for you. ONLY local file references (e.g. './logo.png', 'assets/x.svg') need pre-upload: call image_get_upload_url with src set to the EXACT in-HTML reference, PUT the bytes, then pass the returned tokens via image_tokens below. The server rewrites matching <img src=...> attributes and attaches data-board-resource — do NOT rewrite the HTML yourself. IMPORTANT: ONLY local file references (e.g. './logo.png', 'assets/x.svg') in any part of the HTML like inline, url(), srcset, etc... REQUIRES pre-upload via image_get_upload_url with src set to the EXACT in-HTML reference, then pass the returned tokens via image_tokens below. Fonts: CDN URL only. Author static HTML + CSS only — <script> elements and inline event handlers (onclick etc.) are stripped, so any scripted behavior is silently lost; use CSS states (:hover, :focus) for visual interactivity. Max combined HTML across all slides in one deck: 380,000 bytes — this is the limit that matters for multi-slide decks; budget each slide's HTML accordingly (e.g. ~38,000 bytes/slide for a 10-slide deck). Max per slide: 1,048,576 bytes, but reaching it on any slide in a multi-slide deck will already exceed the combined limit.
Provide exactly one input — PREFER html_tokens (HTML stays out of context). html_contents is a last-resort fallback only when the runtime cannot issue an HTTP PUT at all; HTML size, image count, multi-slide, or not yet having the HTML saved to a file are NOT valid reasons — write it to a file yourself, then PUT it.
Slide sizing: ALWAYS declare the size explicitly in <head> with <meta name="viewport" content="width=W, height=H"> (e.g. width=1920, height=1080), and give the body the same size in CSS so the content lays out to match. Both axes are required — declaring a width alone is worse than declaring nothing, because the height then falls back to 800 and any shorter slide is padded with empty space instead of being fitted. Undeclared slides fall back to 1280x800. Avoid sizing the body in vh/vw or percentages; use the declared pixel size. Slide label: each slide's HTML must include <meta name="screen:name" content="<label>"> in <head> so the slide gets a meaningful display label; without it a generic placeholder is used.
Placement: x/y are board-absolute (board center is (0, 0)), and are for a deck with NO relation to an existing one (e.g. the first deck on a blank board, or the user gave explicit coordinates). Slide order is preserved from the input list. Give it a short descriptive name (e.g. what the user asked for) so the board isn't left with a generic placeholder.
Iterating on an existing deck (passing reference_deck_id means you are iterating — deleting slides, modifying slides, adding new slides, or reordering slides in something already on the board): NEVER compute x/y yourself for this case. Understand the outcome before you start, because it is NOT an in-place edit: the deck you reference stays on the board untouched, and this call adds a SECOND deck below it carrying the edited result. There is no way to patch a slide, or a deck, in place. So html_tokens/html_contents must contain the COMPLETE set of slides for the result, not just the ones that changed, and the user is left with two decks to reconcile. Steps: 1. Call slides_read_html (or canvas_search) and check whether the deck being iterated on is present, i.e. already a real board item. 2. If it is NOT present, it either doesn't exist yet (wrong id, or never created — the common case now) or a create you made for it reported requires_apply=true and is still an unapplied AGR suggestion (rare, legacy path only) — check which before continuing. If pending, tell the user to open it and click 'Apply to canvas' first. If it doesn't exist, stop; do not create a new deck against a nonexistent reference. 3. If it IS present, call slides_read_html with include_html=true to get every existing slide's HTML, then build the full ordered list you want in the result: keep untouched slides by re-uploading their HTML unchanged, modify a slide by re-uploading its edited HTML in the same slot, delete a slide by leaving its HTML out entirely, add a slide by inserting its HTML at the desired position, and reorder slides by arranging the list in the desired final order. 4. Pass that full ordered list as html_tokens/html_contents together with reference_deck_id so the new deck is positioned below the original — best-effort, not pixel-exact — and titled the next version (V2, V3, ...) automatically; you do not need to track or pass a version yourself. The name half of the title carries forward from the original too, so only pass name when you want to change it. Also pass if_not_modified_since (the deck_modified_at from the slides_read_html call in step 3) so an edit made to the original since your read is rejected instead of being silently dropped from your copy — on rejection, re-read, reapply your edit, and retry. 5. Report the result honestly: the original deck is still there and was not modified. Once the new deck is confirmed, tell the user the original is now superseded by the new version and is a leftover duplicate they need to delete themselves — no tool here deletes a deck. Never describe this as having updated, replaced, or edited the original deck.
On return, check requires_apply.
- false: the deck is already a real board item and item_id is the slide_container's own id — use it directly with other tools.
- true: the deck is a pending suggestion, not a real board item — the user must open the returned URL and click 'Apply to canvas' before it exists. The applied deck's real id is NOT item_id and cannot be known in advance. To find it afterwards: call canvas_search (result_mode='overview') and pick the slide_container closest to the x/y this call returned — it will be clearly closer than any other deck already on the board. If nothing is clearly closer, don't guess — list the candidates and ask the user which one.
Whole-deck resend on edits is normal overhead here, even for quick/draft decks — not a sign that something is wrong or that a lighter-weight approach should be used instead.