Edit the content document
update_contentSaves a demo’s content document (steps or hotspots, startScreenId, description) to the draft: either the whole document (content) or JSON Patch operations (ops, RFC 6902, paths like "/steps/2/body" or "/hotspots/0/label"; "-" appends). Validated like the editor (ids unique, screens exist, safe links) and recorded in revision history. Pass the revision you read with get_demo to avoid overwriting someone else’s edit. For single steps, update_step / add_step / delete_step / move_step are simpler.
Steps (guided demos), in order:
tip: { id, kind:"tip", screenId, title ≤200, body ≤2000, spot:{x,y,w,h} (percent of the screenshot), place:"auto"|"top"|"right"|"bottom"|"left", advance:"click"|"next"|"timer"|"narration" ("narration": continues when the step's narration ends), timerMs 1000–60000, hotspot:"pulse"|"outline"|"hidden", zoom, dim, camera?, cursor?, spotlight? } Tooltip camera and motion (optional; left out = the demo default in settings.motion): "camera": { mode?: "auto" (zoom to the spot) | "frame" (show camera.frame) | "off", frame?: {x,y,w,h} percent of the screenshot (fitted to the stage, 1–4× zoom), transition?: "glide"|"cut", speed?: "slow"|"normal"|"fast" }, "cursor": true|false (animated cursor that moves to the spot and clicks), "spotlight": "rounded"|"circle" (the cutout shape with dim). Without camera.mode, zoom: true means "auto".
intro / chapter / cta / outro cards: { id, kind, screenId (or null), title, body, cta ≤60 (button label), ctaUrl (https://, mailto: or tel:, or null), theme:"dark"|"light" }
form (lead form): { id, kind:"form", screenId, title, body, cta, fields: ["email", "name"?, "company"?, "phone"?] (email required), theme } Branching (any guided step but a form): "choices": 2–4 of { id, label ≤60, target: {type:"step",stepId} | {type:"end"} | {type:"url",url} | null } (a card shows them instead of its button; a tooltip shows them as buttons) — set_choices is the easy way. "next" on any step: {type:"step",stepId} or {type:"end"}: where the default path goes after it (absent: the following step); use it to skip ahead, rejoin or end a path. Personalization: titles, bodies, button labels, choice labels, hotspot labels and links can hold {{key}} or {{key|fallback}} (built-ins first_name, last_name, full_name, company, title, email; custom ones declared in content.variables as { key, label, default?, sample? }). Each viewer sees their own values (personalized links, URL parameters), everyone else the fallback or default. Keep the tokens when rewriting copy. Hotspots (clickable demos): { id, screenId, rect:{x,y,w,h}, label ≤120, hint ≤300, target: {type:"screen",screenId} | {type:"url",url} | null }, plus "auto": true on navigation links made by stitch_demo (shown quietly) and "link": a group id the same control shares on every screen. Plus startScreenId and description (≤300, shown under the title on the hosted page). Narration (voiceovers, Pro and Team): "voiceover" on any guided step: { script ≤1000 (what a voice says when the step appears: short, spoken, separate from the tooltip text), source:"ai"|"recorded"|"uploaded", clip: { key, durationMs, cues, hash, voiceId? } | null }. Clickable demos keep it per screen in "screenVoiceovers": { "": same }. "voice": { voiceId (list_voices), speed 0.7–1.2, stability 0–1 } is the demo's AI voice. Make clips with generate_voiceovers; never write a clip key yourself (it must be this demo's audio). Other fields present in the document (newer features) are kept as they are; the server validates the whole document.
Requires: demos:read, demos:write.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ops | No | JSON Patch operations applied to the current document, in order. | |
| demoId | Yes | Demo id (from list_demos), e.g. "d_Ab12Cd34Ef56Gh78". | |
| content | No | The whole document (replaces the current one). | |
| revision | No | The revision from get_demo; the save fails if the demo changed since. |