Jump to
DemoTake MCP tools
DemoTake runs a Model Context Protocol server on your Mac. Your agent calls these 130 tools to plan a take, record it scene by scene, verify it, edit it and export it. Every stateful call takes explicit handles (take_…, scn_…, exp_…), and results come back as structured JSON with a one-line summary.
New to it? Read Connect your agent, give your agent the DemoTake skill, or call demotake_guide once for the whole workflow.
claude mcp add demotake -- /Applications/DemoTake.app/Contents/MacOS/demotake-mcptake_create → capture_start → take_start_scene → pointer_click → assert_text → take_checkpoint → capture_stop → edit_suggest_cuts → edit_apply → export_render
targets_list
List capture targets
List apps, windows, booted iOS Simulator devices and Android emulators/devices DemoTake can record. Use the returned target_id in take_create / capture tools. Windows are listed front-to-back; pick the app you just built. Simulator devices (kind "simulator", target_id "sim:<UDID>") and Android emulators or USB devices (kind "android", target_id "android:<serial>", with model, Android version and API level, screen in dp and pixels; found through adb) are recorded from the device screen itself (DemoTake Agent). Browser windows (Chrome, Edge, Brave, Arc, Safari) carry url and tabs: tabs:"active" (default in DemoTake Agent) reads each window's active tab with Accessibility; tabs:"all" lists every tab's title and URL through Apple Events (DemoTake Agent; macOS asks once per browser for Automation permission). Each window gets its own tabs, matched by title and frame; when two windows can't be told apart, tabs is null and tabs_note says why (never another window's tabs). Windows on another Space or minimized are listed with on_screen:false and a note; they can't be recorded until they're on this Space. DemoTake Studio can't use either: tabs:"active" reads each browser window's address bar with on-screen text recognition (slower; the address bar must be visible), otherwise only window titles are listed. To record a tab, pass take_create {target:{url_contains:"example.com"}}; browser_tab selects or isolates it first.
Parameters
Good to know
An empty list usually means Screen Recording permission is missing; check permissions_status.
{
"name": "targets_list",
"arguments": {}
}{
"targets": [
{
"target_id": "window:4127",
"kind": "window",
"name": "Ledgerly — Invoices",
"app_name": "Ledgerly",
"bundle_id": "com.example.Ledgerly",
"window_title": "Invoices",
"window_id": 4127,
"pid": 51234,
"frame": {
"x": 120,
"y": 84,
"width": 1280,
"height": 800
},
"on_screen": true
},
{
"target_id": "sim:6C1F0B7E-3A2D-4F4B-9C1E-2B7D5A8E9F10",
"kind": "simulator",
"name": "iPhone 17 Pro (iOS 26.0)",
"app_name": "iPhone 17 Pro",
"device_udid": "6C1F0B7E-3A2D-4F4B-9C1E-2B7D5A8E9F10",
"runtime": "iOS 26.0",
"screen": {
"width": 402,
"height": 874,
"scale": 3
},
"on_screen": true
},
{
"target_id": "android:emulator-5554",
"kind": "android",
"name": "Pixel 9 (Android 16 (API 36))",
"app_name": "Pixel 9",
"serial": "emulator-5554",
"runtime": "Android 16 (API 36)",
"screen": {
"width": 411.4,
"height": 914.3,
"scale": 2.625,
"unit": "dp",
"pixels": {
"width": 1080,
"height": 2400
}
},
"on_screen": true
}
]
}browser_tab
Select or isolate a browser tab
Make the tab showing a URL recordable before take_create / capture_start (DemoTake Agent; controls the browser with Apple Events, which macOS asks you to allow once per browser; no Accessibility needed). Finds the tab in any window of Chrome, Edge, Brave, Vivaldi, Arc or Safari, on any Space or profile. action "select": make it the active tab of its window (and bring that window to the front of the browser's windows); a window on another Space can't be selected into view, use isolate. action "isolate": open the tab's page in a new window sized and placed like the original, on this Space, and record that: other tabs and agents can't switch it away, and the person's own windows and tabs stay as they were. Isolate is non-destructive: the original tab is left in place (a page that needs unsaved form input or its back history should be selected instead); close_original:true closes it after the new window opens. Sign-ins carry over (cookies). With several Chrome profiles DemoTake checks that the new window is in the tab's profile; if not, it closes that window, tries once more with the browser in front, and otherwise fails with a message (use select then). Nothing happens when the tab is already active (select) or known to be alone in its window (isolate); when DemoTake can't read that window's tab list, isolate opens the new window rather than assume. Every process of a browser is asked separately (a second Chrome started with its own profile folder too), and changes go to the process that shows the tab; a process Apple Events can't reach is named in the error. Returns target_id of the window to record: pass it, or the same url_contains, to take_create. Arc can only select. Firefox isn't scriptable. DemoTake Studio doesn't control other apps.
Parameters
Good to know
DemoTake Agent only. macOS asks once whether DemoTake may control the browser. action "select" switches the window to the tab instead; close_original: true closes the original tab after isolating.
{
"name": "browser_tab",
"arguments": {
"url_contains": "ledgerly.app",
"action": "isolate"
}
}{
"outcome": "isolated",
"target_id": "window:4188",
"target": {
"target_id": "window:4188",
"kind": "window",
"name": "Google Chrome — Invoices · Ledgerly",
"app_name": "Google Chrome",
"bundle_id": "com.google.Chrome",
"window_title": "Invoices · Ledgerly",
"window_id": 4188,
"pid": 51877,
"frame": {
"x": 0,
"y": 34,
"width": 1440,
"height": 866
},
"on_screen": true
},
"tab": {
"title": "Invoices · Ledgerly",
"url": "https://ledgerly.app/invoices"
},
"notes": [
"The page opened in a new Google Chrome window; the original tab is still where it was."
],
"next": "take_create {target:\"window:4188\", browser:{mode:\"page\"}} (or capture_start on an existing take with target \"window:4188\")."
}targets_inspect
Inspect a target
Details for one target: app version/build, its windows, and what DemoTake can do with it (execution mode).
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
{
"name": "targets_inspect",
"arguments": {
"target": "window:4127"
}
}{
"target": {
"target_id": "window:4127",
"kind": "window",
"name": "Ledgerly — Invoices",
"app_name": "Ledgerly",
"bundle_id": "com.example.Ledgerly",
"window_title": "Invoices",
"window_id": 4127,
"pid": 51234,
"frame": {
"x": 120,
"y": 84,
"width": 1280,
"height": 800
},
"on_screen": true
},
"capabilities": {
"mode": "background_semantic",
"mode_explanation": "Understands and operates the app in the background.",
"edition": "agent",
"can_capture": true,
"can_observe_accessibility": true,
"can_perform_semantic_actions": true,
"can_synthesize_input": true,
"is_minimized": false,
"is_on_screen": true,
"notes": [
"Accessibility tree available (212+ elements, 31 actionable). Clicks and typing run in the background; your mouse stays yours."
]
},
"app_version": "2.3.1",
"build_number": "418",
"windows": [
{
"target_id": "window:4127",
"kind": "window",
"name": "Ledgerly — Invoices",
"app_name": "Ledgerly",
"bundle_id": "com.example.Ledgerly",
"window_title": "Invoices",
"window_id": 4127,
"pid": 51234,
"frame": {
"x": 120,
"y": 84,
"width": 1280,
"height": 800
},
"on_screen": true
}
]
}targets_capabilities
Target capabilities
How a take of this target will run: background_semantic (DemoTake operates the app via Accessibility without touching your mouse), background_capture_only (you/your agent operate it, DemoTake records), or foreground_required. Also what this Mac can record beside the screen: presenter_camera (camera access and the cameras, with ids for capture_start {camera_device}), and android (DemoTake Agent: whether adb was found and which Android emulators and devices are connected). Without target, only those.
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
{
"name": "targets_capabilities",
"arguments": {
"target": "app:com.example.Ledgerly"
}
}{
"mode": "background_semantic",
"mode_explanation": "Understands and operates the app in the background.",
"edition": "agent",
"can_capture": true,
"can_observe_accessibility": true,
"can_perform_semantic_actions": true,
"can_synthesize_input": true,
"is_minimized": false,
"is_on_screen": true,
"notes": [
"Accessibility tree available (212+ elements, 31 actionable). Clicks and typing run in the background; your mouse stays yours."
],
"presenter_camera": {
"authorization": "granted",
"cameras": [
{
"id": "0x1420000005ac8600",
"name": "FaceTime HD Camera",
"default": true
}
],
"notes": "capture_start {camera:true} records the presenter camera beside the screen (one file per scene); presenter_set styles it; presenter_import adds a video recorded elsewhere."
},
"android": {
"adb": "/Users/sam/Library/Android/sdk/platform-tools/adb",
"available": true,
"devices": [
"Pixel 9 (Android 16 (API 36))"
],
"notes": "Android targets are listed by targets_list as android:<serial>; DemoTake records the device screen and acts with adb input."
}
}permissions_status
Permission status
macOS permissions DemoTake needs (Screen Recording, Accessibility, Microphone), the edition/version running, and its licence (licence: the same as licence_status).
Parameters
None.
{
"name": "permissions_status",
"arguments": {}
}{
"screen_recording": "granted",
"accessibility": "granted",
"microphone": "not_determined",
"camera": "not_determined",
"edition": "agent",
"version": "1.2.0",
"ui_control_available": true,
"licence": {
"edition": "agent",
"version": "1.2.0",
"state": "licensed",
"plan": "Local",
"source": "License key",
"updates_until": "2027-10-26",
"recording_blocked": false,
"export_blocked": false,
"watermarked_exports": false
}
}licence_status
Licence status
Whether DemoTake can record and export right now, and why not: edition and version, licence state (licensed, trial, trial_ended, trial_not_started), plan and what unlocks it (license key, cloud plan, App Store), trial days left (trial exports are watermarked), the updates window (newest builds the licence covers), and recording_blocked / export_blocked with the reason and what the person can do. Check it before a long recording. Also reachable as license_status.
Parameters
None.
Good to know
Also reachable as license_status; permissions_status returns the same object as licence.
{
"name": "licence_status",
"arguments": {}
}{
"edition": "agent",
"version": "1.2.0",
"state": "trial",
"trial_days_left": 2,
"recording_blocked": false,
"export_blocked": false,
"watermarked_exports": true,
"signed_in": false
}project_list
List projects
Projects in the DemoTake library.
Parameters
None.
{
"name": "project_list",
"arguments": {}
}{
"projects": [
{
"project_id": "prj_m119ecc0t4w8nq2h",
"name": "Ledgerly",
"summary": "Invoicing for freelancers",
"updated_at": "2026-09-26T10:14:03Z"
},
{
"project_id": "prj_m0zt3hq0f6kn2bwd",
"name": "My Demos",
"updated_at": "2026-09-12T16:31:47Z"
}
]
}project_create
Create project
Create a project (a folder of takes, usually one per product). take_create can also create one implicitly via project_name.
Parameters
{
"name": "project_create",
"arguments": {
"name": "Ledgerly",
"summary": "Invoicing for freelancers"
}
}{
"project_id": "prj_m119ecc0t4w8nq2h",
"name": "Ledgerly",
"summary": "Invoicing for freelancers",
"updated_at": "2026-08-27T09:40:00Z"
}take_list
List takes
Takes in the library, newest first. Existing takes can be re-recorded after UI changes instead of starting over.
Parameters
project_id.{
"name": "take_list",
"arguments": {
"project_id": "prj_m119ecc0t4w8nq2h",
"limit": 10
}
}{
"takes": [
{
"take_id": "take_m3ehbyc0k7qv2x9d",
"project_id": "prj_m119ecc0t4w8nq2h",
"name": "Create invoice",
"goal": "Create an invoice for a new customer and export it as PDF.",
"purpose": "marketing",
"status": "verified",
"scenes": 3,
"scenes_recorded": 3,
"duration": 41.8,
"assertions": "3/3 passed",
"target": "Ledgerly — Invoices",
"updated_at": "2026-09-26T10:14:03Z",
"package_path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ehbyc0k7qv2x9d.take"
}
],
"total": 1
}take_get
Get take
Full state of a take: scenes, assertions, edits, exports, recent trace events and the suggested next step.
Parameters
take_id.Good to know
Follow the next string when unsure what to call; it tracks the take's status.
{
"name": "take_get",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"recent_events": 2
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"project_id": "prj_m119ecc0t4w8nq2h",
"name": "Create invoice",
"goal": "Create an invoice for a new customer and export it as PDF.",
"purpose": "marketing",
"status": "verified",
"ai_mode": "byo_agent",
"execution_mode": "background_semantic",
"target": {
"target_id": "window:4127",
"kind": "window",
"name": "Ledgerly — Invoices",
"bundle_id": "com.example.Ledgerly",
"app_name": "Ledgerly",
"window_title": "Invoices",
"window_id": 4127,
"pid": 51234
},
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"intent": "Sign in with the demo account",
"status": "recorded",
"attempts": 1,
"duration": 11.8,
"checkpoint": "Invoices list visible"
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio",
"status": "recorded",
"attempts": 2,
"duration": 24.3,
"checkpoint": "Invoice INV-0042 open with 2 line items"
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 3,
"title": "Export PDF",
"intent": "Export the invoice as PDF",
"status": "recorded",
"attempts": 1,
"duration": 16.3,
"checkpoint": "PDF saved to Downloads"
}
],
"assertions": [
{
"assertion_id": "ast_m3ehfkj0r9vb4ngs",
"kind": "text",
"title": "Invoice created",
"status": "passed",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"expectation": "Invoice INV-0042 created",
"evidence": "Text present in accessibility tree",
"take_time": 34.62
}
],
"assertions_passed": "3/3",
"event_count": 27,
"recent_events": [
{
"event_id": "evt_m3ehwq1g2cbn7fyh",
"scene_id": "scn_m3ehbzbap5hn0cxv",
"time": 9.84,
"kind": "click",
"beat": "interaction",
"label": "Click Export PDF",
"source": "agent"
},
{
"event_id": "evt_m3ehwt8g5jzr3mkp",
"scene_id": "scn_m3ehbzbap5hn0cxv",
"time": 12.31,
"kind": "checkpoint_created",
"beat": "confirmation",
"label": "PDF saved to Downloads",
"source": "system"
}
],
"source_duration": 52.4,
"outputs": [
"16:9",
"9:16"
],
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
},
"trims": 1
},
"exports": [
{
"job_id": "exp_m3ej19z0b8mc3rkf",
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "16:9",
"status": "completed",
"progress": 1,
"output_path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ehbyc0k7qv2x9d.take/exports/create-invoice-16x9-20260926-101204.mp4",
"duration": 41.8,
"sha256": "3b7e5a0c9d14f2e86a1b47c3d9e0f5a2b6c8d1e4f7a0b3c6d9e2f5a8b1c4d7e0",
"completed_at": "2026-09-26T10:12:31Z"
}
],
"updated_at": "2026-09-26T10:14:03Z",
"next": "Edit (edit_* / edit_command), then export_render."
}take_create
Create take
Create a Semantic Take: a planned, re-recordable demo made of scenes (story beats bounded by stable app states). Returns take_id. Typical flow: take_create → (take_rehearse) → capture_start → per scene: take_start_scene → act → assert_* → take_checkpoint → capture_stop → edit_* → export_render. Web apps: browser:{mode:"page", address_bar:"drawn"} records only the page area of the browser window and draws a clean address bar (domain + path, never the query string) from the tab's URL instead of the real browser chrome: no tab strip, no automation infobars ("Chrome is being controlled by automated test software", "… is debugging this browser"); the crop follows the page as infobars come and go, and the address bar follows navigations and redirects across domains. target:{url_contains:"example.com"} records the window whose tab shows that URL. DemoTake Agent reads the page area and URL with Accessibility; DemoTake Studio finds them with on-screen text recognition of the toolbar (set browser.page_region if it's off, and log URL changes with take_log_event {kind:"navigation", url} if the address bar isn't readable). Platform review videos: purpose:"reviewer", platform (tiktok, meta_app_review, google_oauth, apple_review), app_url (the website URL in the review form) and scopes (permissions the app requests); mark the scene that shows each scope with scenes[].shows_scopes. review_check verifies the take against them and writes a paste-ready checklist. Presets caption each scene with its intent; TikTok exports default to quality:"upload", max_mb:45 (TikTok accepts MP4/MOV up to 50 MB).
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
3 fields
5 fields
4 fields
7 fields
cameras.target.12 fields
project_id.Good to know
With no scenes but a goal, DemoTake plans the scenes for you.
{
"name": "take_create",
"arguments": {
"name": "Create invoice",
"goal": "Create an invoice for a new customer and export it as PDF.",
"purpose": "marketing",
"target": "window:4127",
"scenes": [
{
"title": "Sign in",
"intent": "Sign in with the demo account"
},
{
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio"
},
{
"title": "Export PDF",
"intent": "Export the invoice as PDF"
}
],
"outputs": [
"16:9",
"9:16"
],
"project_name": "Ledgerly"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"project_id": "prj_m119ecc0t4w8nq2h",
"name": "Create invoice",
"goal": "Create an invoice for a new customer and export it as PDF.",
"purpose": "marketing",
"status": "draft",
"ai_mode": "byo_agent",
"execution_mode": "background_semantic",
"target": {
"target_id": "window:4127",
"kind": "window",
"name": "Ledgerly — Invoices",
"bundle_id": "com.example.Ledgerly",
"app_name": "Ledgerly",
"window_title": "Invoices",
"window_id": 4127,
"pid": 51234
},
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"intent": "Sign in with the demo account",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 3,
"title": "Export PDF",
"intent": "Export the invoice as PDF",
"status": "pending",
"attempts": 0
}
],
"assertions": [],
"assertions_passed": "0/0",
"event_count": 0,
"source_duration": 0,
"outputs": [
"16:9",
"9:16"
],
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
}
},
"updated_at": "2026-09-26T09:40:00Z",
"next": "Optionally take_rehearse to dry-run, then capture_start."
}take_plan
Update take plan
Replace the scene list and/or change goal, purpose or target of a take that hasn't been recorded yet (recorded scenes keep their media when titles match).
Parameters
3 fields
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.Good to know
Scenes whose titles match an existing scene keep their id and any recorded footage.
{
"name": "take_plan",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"scenes": [
{
"title": "Sign in",
"intent": "Sign in with the demo account"
},
{
"title": "Add customer",
"intent": "Add Northwind Studio as a customer"
},
{
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio"
},
{
"title": "Export PDF",
"intent": "Export the invoice as PDF"
}
],
"goal": "Add a customer, invoice them and export the invoice as PDF."
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"goal": "Add a customer, invoice them and export the invoice as PDF.",
"purpose": "marketing",
"status": "draft",
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"intent": "Sign in with the demo account",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehgm30x4ty6pwa",
"index": 2,
"title": "Add customer",
"intent": "Add Northwind Studio as a customer",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 3,
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 4,
"title": "Export PDF",
"intent": "Export the invoice as PDF",
"status": "pending",
"attempts": 0
}
],
"assertions_passed": "0/0",
"outputs": [
"16:9",
"9:16"
],
"next": "Optionally take_rehearse to dry-run, then capture_start."
}take_rehearse
Start rehearsal
Dry-run the flow without recording (preview only). Walk every scene with your own tools or ui_* tools, then call take_rehearse_finish. Rehearsal events are kept separately and never rendered.
Parameters
take_id.{
"name": "take_rehearse",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "rehearsing",
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 3,
"title": "Export PDF",
"status": "pending",
"attempts": 0
}
],
"assertions_passed": "0/0",
"next": "Walk through each scene, then take_rehearse_finish with findings."
}take_rehearse_finish
Finish rehearsal
End the rehearsal, recording what you learned (slow screens, popups, required data).
Parameters
take_id.{
"name": "take_rehearse_finish",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"findings": "Sign-in shows a 2 s spinner; the PDF export sheet needs a customer selected first."
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "rehearsed",
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"status": "rehearsed",
"attempts": 0,
"rehearsed_duration": 9.2
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"status": "rehearsed",
"attempts": 0,
"rehearsed_duration": 21.7
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 3,
"title": "Export PDF",
"status": "rehearsed",
"attempts": 0,
"rehearsed_duration": 14.1
}
],
"assertions_passed": "0/0",
"next": "capture_start, then for each scene: take_start_scene → act → assert_* → take_checkpoint."
}take_start_scene
Start scene
Begin recording a scene (a new segment). Ends any scene still running. Capture must be running (capture_start). Get the app into the scene's starting state first.
Parameters
scene_id is accepted as an alias.scene.take_id.Good to know
Capture must already be running (capture_start); the scene can be a scn_ handle, its exact title or its 1-based index.
{
"name": "take_start_scene",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene": "Create invoice"
}
}{
"scene": {
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio",
"status": "recording",
"attempts": 1
},
"take_status": "recording",
"next": "Scene \"Create invoice\" is recording: act, assert, then take_checkpoint."
}take_checkpoint
Checkpoint scene
Finish the running scene at a stable state and describe it ("Dashboard visible with 3 projects"). Checkpoints are where a failed later scene can be retried from. screenshot:true also captures the stable screen into a Screenshot Set (for App Store / Google Play screenshots) named after the take unless screenshot_set is given.
Parameters
scene.take_id.Good to know
Add screenshot: true to also capture the stable screen as a store screenshot, into a Screenshot Set named after the take (or screenshot_set). The result then has a screenshot {set_id, shot_id, path}.
{
"name": "take_checkpoint",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"description": "Invoice INV-0042 open with 2 line items"
}
}{
"scene": {
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio",
"status": "recorded",
"attempts": 1,
"duration": 24.3,
"checkpoint": "Invoice INV-0042 open with 2 line items"
},
"take_status": "recording",
"next": "take_start_scene \"Export PDF\" next."
}take_retry_scene
Retry scene
Mark a scene failed and prepare a fresh attempt. Only that scene is re-recorded; other scenes keep their footage. Restore the scene's starting state, then take_start_scene again.
Parameters
scene_id is accepted as an alias.scene.take_id.Good to know
Put the app back into the scene's starting state before calling take_start_scene again.
{
"name": "take_retry_scene",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene": "2",
"reason": "Typed the wrong customer name"
}
}{
"scene": {
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"intent": "Create an invoice for Northwind Studio",
"status": "failed",
"attempts": 1,
"notes": "Typed the wrong customer name"
},
"take_status": "recording",
"next": "take_start_scene \"Create invoice\" next."
}take_log_event
Log trace event
Report what you just did with your OWN computer-use tools (BYO agent), so DemoTake can place the presentation pointer, zoom and edit semantically. Call right after each meaningful action. Prefer element or screen_point so the pointer lands correctly. iOS Simulator and Android targets: report taps as kind "click" (or "tap") with point normalized to the device screen (0…1), or device_point in device points (Android: dp); they render as a touch indicator. Set redact=true for anything secret; never put passwords in text. source says who acted: "agent" (you, default), "automation" (a browser/UI automation tool you drive, e.g. Playwright or Claude in Chrome) or "human" (the person, e.g. signing in); reviewer packs list it. take_relabel_events fixes the source of events already logged (e.g. clicks DemoTake observed as human input). Browser takes: log kind "navigation" with url when the page changes and DemoTake can't read the URL itself (DemoTake Studio); it is drawn in the address bar from that moment.
Parameters
2 fields
2 fields
2 fields
6 fields
4 fields
scene.take_id.Good to know
Never put passwords in text; set redact to true for anything secret. For iOS Simulator taps use kind "tap" with a device-normalized point or device_point.
{
"name": "take_log_event",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"kind": "click",
"label": "Click New Invoice",
"screen_point": {
"x": 1302,
"y": 131
},
"element": {
"role": "AXButton",
"title": "New Invoice",
"identifier": "new-invoice"
}
}
}{
"event_id": "evt_m3ehep8g6yzk1mcd",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"time": 3.42,
"kind": "click",
"beat": "interaction",
"label": "Click New Invoice",
"source": "agent"
}take_relabel_events
Re-label who acted
Change the source of recorded events: who acted, as the reviewer pack reports it. Use it when DemoTake observed clicks as human input that a browser automation tool made, or to mark a person's sign-in as human. Select events by event_ids (timeline_get / take_get list them), or by scene with an optional scene-local start/end (source seconds), kinds and from_source. Scene lifecycle, checkpoint and assertion events keep their source.
Parameters
scene.take_id.Good to know
Select events by event_ids, or by scene with optional scene-local start/end, kinds and from_source. Scene, checkpoint and assertion events keep their source.
{
"name": "take_relabel_events",
"arguments": {
"take_id": "take_m5tq2r8c0vk7wd3n",
"scene": "Connect account",
"kinds": [
"click",
"type"
],
"from_source": "human",
"source": "automation"
}
}{
"changed": 2,
"events": [
{
"event_id": "evt_m5tq3c1k7fz2nb8r",
"kind": "click",
"source": "automation",
"label": "Connect TikTok"
},
{
"event_id": "evt_m5tq3c2a9wq4hd1m",
"kind": "click",
"source": "automation",
"label": "Continue"
}
]
}capture_start
Start capture
Start recording the take's target window (background-safe: the window may be covered, but must not be minimized). By default the first pending scene starts immediately.
Parameters
cameras (default: the take's choice, else the system camera).5 fields
4 fields
take_id.Good to know
Pass auto_start_scene: false to arm the capture and start each scene yourself with take_start_scene.
{
"name": "capture_start",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"fps": 60
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"state": "recording",
"take_status": "recording",
"current_scene_id": "scn_m3ehbzb8a1fr6mzp",
"current_scene": "Sign in",
"segment_elapsed": 0,
"total_recorded": 0,
"scenes_recorded": 0,
"scenes_total": 3,
"execution_mode": "background_semantic"
}capture_pause
Pause capture
Pause recording (e.g. while fixing app state). Paused time is not recorded. With resume_when {text, timeout} it resumes by itself when that text appears on the target window (on-screen text recognition; works in both editions): use it for slow server work instead of polling, e.g. {text:"Upload complete", timeout:300}. Events: handoff_started, then handoff_ended {outcome}. take_wait does the same and can add an "A moment later" card.
Parameters
2 fields
take_id.{
"name": "capture_pause",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"state": "paused",
"take_status": "paused",
"current_scene_id": "scn_m3ehbzb9d3kw7tbe",
"current_scene": "Create invoice",
"segment_elapsed": 12.3,
"total_recorded": 24.1,
"scenes_recorded": 1,
"scenes_total": 3,
"execution_mode": "background_semantic"
}capture_resume
Resume capture
Resume a paused recording. Also ends a hand-off or wait in progress (take_handoff / take_wait).
Parameters
take_id.{
"name": "capture_resume",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"state": "recording",
"take_status": "recording",
"current_scene_id": "scn_m3ehbzb9d3kw7tbe",
"current_scene": "Create invoice",
"segment_elapsed": 12.3,
"total_recorded": 24.1,
"scenes_recorded": 1,
"scenes_total": 3,
"execution_mode": "background_semantic"
}take_handoff
Hand off to a person
A step that is the person's to do (signing in, pressing Allow on a consent screen): pauses the capture and shows message to the person inside DemoTake (a banner with a Resume button in the recording UI, on top of other windows), not only in your chat. It resumes automatically when resume_when.text appears on the target window (on-screen text recognition, which needs no Accessibility, so it works in DemoTake Studio too), when the person presses Resume, or when you call capture_resume. blur:true keeps recording instead and blurs the whole frame for that span (shows that a sign-in happened without showing it). Poll events_poll for handoff_ended (outcome text_appeared, resumed_by_person, resumed_by_agent, timed_out); capture_status shows the hand-off. A scene must be recording.
Parameters
2 fields
take_id.Good to know
Poll events_poll for handoff_ended (outcome text_appeared, resumed_by_person, resumed_by_agent or timed_out). blur:true keeps recording, blurred, instead of pausing. Text recognition needs no Accessibility, so it works in DemoTake Studio too.
{
"name": "take_handoff",
"arguments": {
"take_id": "take_m5tq2r8c0vk7wd3n",
"message": "Sign in to TikTok and stop at the consent screen",
"resume_when": {
"text": "wants to access your account",
"timeout": 600
}
}
}{
"take_id": "take_m5tq2r8c0vk7wd3n",
"state": "paused",
"take_status": "recording",
"current_scene_id": "scn_m5tq2r9d4bx1kf6p",
"current_scene": "Authorize",
"segment_elapsed": 6.4,
"total_recorded": 41.2,
"scenes_recorded": 2,
"scenes_total": 5,
"execution_mode": "background_capture_only",
"pointer": "off",
"browser_url": "https://www.tiktok.com/login",
"handoff": {
"handoff_id": "hof_m5tq3k0p2vd8xc4a",
"kind": "human",
"message": "Sign in to TikTok and stop at the consent screen",
"resume_when": {
"text": "wants to access your account",
"timeout": 600
},
"scene_id": "scn_m5tq2r9d4bx1kf6p",
"started_at": "2026-10-26T14:08:12Z"
}
}take_wait
Wait for a slow result
Slow server work (an upload, a background job): pauses the capture so the wait isn't recorded, and resumes when resume_when.text appears on the target window (on-screen text recognition), or on capture_resume. card:"A moment later" captions the moment the recording picks up (for 2 seconds; an ordinary caption you can edit or revert). Events: handoff_started / handoff_ended.
Parameters
2 fields
take_id.Good to know
capture_pause {resume_when} does the same without a card. The card is an ordinary caption you can edit or revert.
{
"name": "take_wait",
"arguments": {
"take_id": "take_m5tq2r8c0vk7wd3n",
"resume_when": {
"text": "Draft sent to your inbox",
"timeout": 300
},
"card": "A moment later",
"message": "Waiting for the upload to finish"
}
}{
"take_id": "take_m5tq2r8c0vk7wd3n",
"state": "paused",
"take_status": "recording",
"current_scene_id": "scn_m5tq2ra1h8nz3qte",
"current_scene": "Draft in TikTok inbox",
"segment_elapsed": 3.1,
"total_recorded": 58.7,
"scenes_recorded": 4,
"scenes_total": 5,
"handoff": {
"handoff_id": "hof_m5tq4a7e1rb3yz6k",
"kind": "wait",
"message": "Waiting for the upload to finish",
"resume_when": {
"text": "Draft sent to your inbox",
"timeout": 300
},
"card": "A moment later",
"scene_id": "scn_m5tq2ra1h8nz3qte",
"started_at": "2026-10-26T14:11:40Z"
}
}capture_stop
Stop capture
Stop recording, finalize the take and open it in the editor. Unrecorded scenes stay pending and can be recorded later.
Parameters
take_id.Good to know
The take finishes processing in the background (automatic redaction of sensitive text, final status); take_get shows when it is ready.
{
"name": "capture_stop",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"state": "idle",
"take_status": "processing",
"segment_elapsed": 0,
"total_recorded": 52.4,
"scenes_recorded": 3,
"scenes_total": 3,
"execution_mode": "background_semantic",
"next": "Processing; poll take_get or events_poll."
}capture_status
Capture status
Recording state, current scene and elapsed time, whether DemoTake draws its pointer, a hand-off or wait in progress, the browser URL being shown, and notes.
Parameters
take_id.Good to know
state is one of idle, previewing, rehearsing, recording or paused.
{
"name": "capture_status",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"state": "recording",
"take_status": "recording",
"current_scene_id": "scn_m3ehbzb9d3kw7tbe",
"current_scene": "Create invoice",
"segment_elapsed": 8.6,
"total_recorded": 20.4,
"scenes_recorded": 1,
"scenes_total": 3,
"execution_mode": "background_semantic"
}observe_state
Observe UI state
Compact text outline of the target window's UI (roles, titles, values, #identifiers, normalized frames), secrets redacted. Much cheaper than a screenshot; use it to find elements for pointer_click / assert_element. Falls back to OCR text when Accessibility isn't available (DemoTake Studio). Android targets: the app's UI hierarchy (uiautomator: classes, texts, resource ids, frames normalized to the screen); apps that draw their own UI (games, Flutter) fall back to OCR of the device screen.
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.Good to know
The outline itself arrives as the text content (lines like - button "New Invoice" #new-invoice @(0.88,0.06 0.09x0.04)); structuredContent carries only this metadata.
{
"name": "observe_state",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"max_depth": 12
}
}{
"source": "ax",
"window_title": "Invoices",
"focused": {
"role": "AXTextField",
"label": "Customer",
"value": "Northwind Studio",
"identifier": "customer-field",
"selector": "AXWindow/AXGroup[1]/AXTextField[0]",
"frame": {
"x": 0.312,
"y": 0.184,
"width": 0.34,
"height": 0.042
}
},
"node_count": 143,
"captured_at": "2026-09-26T09:47:12Z"
}observe_snapshot
Snapshot
A still image of the target window (secrets detected by OCR are blurred before it leaves DemoTake). Use sparingly; observe_state is cheaper.
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.Good to know
The PNG itself is returned as an image content block next to this JSON.
{
"name": "observe_snapshot",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"max_pixel_size": 1024
}
}{
"path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ehbyc0k7qv2x9d.take/snapshots/….png",
"width": 1024,
"height": 640,
"redacted_regions": 1
}observe_wait_for
Wait for UI
Wait until an element appears/disappears, text becomes visible, or the UI is idle. During a recording the wait becomes a trace event that edits can compress ("remove waits > 1 s").
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
6 fields
element to disappear instead.target.12 fields
take_id.Good to know
Give exactly one of element, text or idle_seconds; a timeout returns satisfied: false with the summary "Timed out after 15.0s."
{
"name": "observe_wait_for",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"text": "Invoice saved",
"timeout": 15
}
}{
"satisfied": true
}pointer_click
Click
Click an element (preferred) or a normalized point in the target window. Background-safe via Accessibility when possible (your mouse is untouched); the presentation pointer is synthesized in the render. Android targets: adb input on the device (an element is found in the UI hierarchy, or by its text on screen; button right = long press). Protected apps (terminals, password managers, System Settings, DemoTake) are refused unless the user allowed them in Settings.
Parameters
6 fields
2 fields
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.Good to know
If no background-safe path exists the call fails and says so; retry with allow_foreground: true only when the user isn't working.
{
"name": "pointer_click",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"element": {
"role": "button",
"title": "New Invoice"
},
"label": "Click New Invoice"
}
}{
"success": true,
"method": "semantic",
"element": {
"role": "AXButton",
"title": "New Invoice",
"identifier": "new-invoice",
"selector": "AXWindow/AXToolbar[0]/AXButton[1]",
"frame": {
"x": 0.84,
"y": 0.038,
"width": 0.094,
"height": 0.042
}
},
"point": {
"x": 0.887,
"y": 0.059
}
}pointer_move
Move pointer
Move the real pointer to a normalized point (hover menus/tooltips). Needs foreground control, which only the user can turn on in DemoTake's Settings; for presentation motion you don't need this — the pointer is synthesized from clicks.
Parameters
2 fields
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.Good to know
Requires allow_foreground: true because it takes the real pointer; ask the user first if they are working.
{
"name": "pointer_move",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"point": {
"x": 0.42,
"y": 0.31
},
"label": "Hover over the due date",
"allow_foreground": true
}
}{
"success": true,
"method": "foreground_input",
"point": {
"x": 0.42,
"y": 0.31
}
}pointer_scroll
Scroll
Scroll the target (positive dy scrolls content down). Amounts are in lines. Android targets: a swipe (10 lines ≈ a third of the screen) at point. Web pages in a browser (Chrome) don't scroll through Accessibility in the background: there it needs foreground control (only the user turns it on in Settings), or bring an element into view with ui_perform_action {element, action:"AXScrollToVisible"}.
Parameters
2 fields
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.{
"name": "pointer_scroll",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"dy": 5,
"point": {
"x": 0.5,
"y": 0.6
},
"label": "Scroll to line items"
}
}{
"success": true,
"method": "semantic",
"element": {
"role": "AXScrollArea",
"selector": "AXWindow/AXGroup[1]/AXScrollArea[0]",
"frame": {
"x": 0.25,
"y": 0.12,
"width": 0.75,
"height": 0.88
}
},
"point": {
"x": 0.5,
"y": 0.6
}
}keyboard_type
Type text
Type text into the focused element or the element matching element. Secure fields are detected automatically and never logged; set secure=true for any secret anyway. In web pages the field is read back: if the page ignored background typing (Chrome takes keystrokes only in its front window), DemoTake inserts the text through Accessibility, and otherwise fails saying foreground control is needed rather than reporting success. Background-safe via Accessibility when possible (your mouse is untouched); the presentation pointer is synthesized in the render. Android targets: adb input on the device (an element is found in the UI hierarchy, or by its text on screen; button right = long press). Protected apps (terminals, password managers, System Settings, DemoTake) are refused unless the user allowed them in Settings.
Parameters
6 fields
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.{
"name": "keyboard_type",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"text": "Northwind Studio",
"element": {
"identifier": "customer-field"
},
"label": "Enter customer name"
}
}{
"success": true,
"method": "semantic",
"element": {
"role": "AXTextField",
"label": "Customer",
"value": "Northwind Studio",
"identifier": "customer-field",
"selector": "AXWindow/AXGroup[1]/AXTextField[0]",
"frame": {
"x": 0.312,
"y": 0.184,
"width": 0.34,
"height": 0.042
}
},
"point": {
"x": 0.482,
"y": 0.205
}
}keyboard_key
Press keys
Press a key or combo: "return", "tab", "escape", "cmd+s", "cmd+shift+n", "down". Key presses are delivered to the target app's process. Android targets: adb key events, plus "back", "home", "recents", "menu", "ctrl+a" (Android 13+) or any KEYCODE_….
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.Good to know
Modifiers are joined with +, e.g. "cmd+shift+n"; keys go to the target app's process.
{
"name": "keyboard_key",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"keys": "cmd+s",
"label": "Save invoice"
}
}{
"success": true,
"method": "background_input"
}ui_perform_action
Accessibility action
Perform a named accessibility action on an element: AXPress, AXShowMenu, AXIncrement, AXDecrement, AXConfirm, AXCancel, AXRaise, AXPick.
Parameters
6 fields
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.{
"name": "ui_perform_action",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"element": {
"role": "popupbutton",
"title": "Currency"
},
"action": "AXShowMenu"
}
}{
"success": true,
"method": "semantic",
"element": {
"role": "AXPopUpButton",
"title": "Currency",
"value": "USD",
"identifier": "currency-picker",
"selector": "AXWindow/AXGroup[1]/AXPopUpButton[0]",
"frame": {
"x": 0.66,
"y": 0.184,
"width": 0.12,
"height": 0.042
}
},
"point": {
"x": 0.72,
"y": 0.205
}
}ui_focus
Focus target
Bring the target app/window to the front. Only needed for foreground-only actions; recording works while the window is covered.
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
take_id.{
"name": "ui_focus",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"success": true,
"method": "semantic",
"message": "Ledgerly brought to front."
}assert_element
Assert element
Proof Assertion: DemoTake checks that an element exists right now (Accessibility, or OCR in Studio). Attached to the running scene and shown in the proof manifest.
Parameters
6 fields
scene.take_id.{
"name": "assert_element",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"element": {
"role": "button",
"title": "Send Invoice"
},
"title": "Invoice created"
}
}{
"assertion_id": "ast_m3ehfkj0r9vb4ngs",
"kind": "element",
"title": "Invoice created",
"status": "passed",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"expectation": "{\"role\":\"button\",\"title\":\"Send Invoice\"}",
"evidence": "Found AXButton “Send Invoice”",
"take_time": 34.62,
"detail": "role=button title=\"Send Invoice\""
}assert_text
Assert text
Proof Assertion: DemoTake checks that text is visible in the target window (accessibility values, then OCR).
Parameters
scene.take_id.Good to know
Without a title the assertion is named “<text>” visible.
{
"name": "assert_text",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"text": "Invoice INV-0042 created",
"title": "Invoice created"
}
}{
"assertion_id": "ast_m3ehfkj0r9vb4ngs",
"kind": "text",
"title": "Invoice created",
"status": "passed",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"expectation": "Invoice INV-0042 created",
"evidence": "Text present in accessibility tree",
"take_time": 34.62
}assert_state
Assert state
Record your own verdict about app state (e.g. checked via API or database). Give honest evidence; it appears in the proof manifest.
Parameters
scene.take_id.{
"name": "assert_state",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"title": "Invoice saved",
"expectation": "Invoice INV-0042 exists in the database with total $1,250.00",
"passed": true,
"evidence": "GET /api/invoices/INV-0042 returned 200 with total 1250.00"
}
}{
"assertion_id": "ast_m3ehfmq0t6cx2jwn",
"kind": "state",
"title": "Invoice saved",
"status": "passed",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"expectation": "Invoice INV-0042 exists in the database with total $1,250.00",
"evidence": "GET /api/invoices/INV-0042 returned 200 with total 1250.00",
"take_time": 36.1
}assert_success
Assert flow success
Final verdict that the whole flow succeeded. A take with all assertions passing becomes Proof Verified.
Parameters
take_id.{
"name": "assert_success",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"passed": true,
"evidence": "Invoice INV-0042 was created and the PDF was saved to Downloads."
}
}{
"assertion_id": "ast_m3ehz2k0w8fp5cdq",
"kind": "success",
"title": "Flow completed successfully",
"status": "passed",
"expectation": "Flow completed successfully",
"evidence": "Invoice INV-0042 was created and the PDF was saved to Downloads."
}assert_check
Re-check assertion
Re-evaluate an element/text assertion now.
Parameters
take_id.{
"name": "assert_check",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"assertion_id": "ast_m3ehfkj0r9vb4ngs"
}
}{
"assertion_id": "ast_m3ehfkj0r9vb4ngs",
"kind": "text",
"title": "Invoice created",
"status": "failed",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"expectation": "Invoice INV-0042 created",
"evidence": "Text not found",
"take_time": 41.2
}edit_apply
Apply edits
Apply several non-destructive edits at once (source media is never modified). Each operation is {op, …fields}. ops: trim_scene{scene,head,tail} set_speed{scene,start,end,rate} compress_waits{enabled,threshold,compressed_to} edit_event{event_id,action:keep|compress|remove,mask,compress_to,hold_after,camera} hold{scene,at,duration | event_id,duration} camera{scene,mode:wide|focus|punch|none,start,end,region} clear_camera{scene} camera_intensity{intensity} redact{region,scene,start,end,style,reason} remove_redaction{id} set_redaction{id,enabled,region,style} (enabled:false turns off a Secret Guard suggestion the detector got wrong; it stays listed so it isn't suggested again; ids are in take_get's edits.redactions) pointer{personality,shape,size,color,highlight_ring,click_effects} caption{scene,text, start,end | at:scene_start|checkpoint|event, event_id, checkpoint_id, duration, offset} captions_from_intents{scenes,duration,replace} captions_enabled{enabled} remove_scene{scene} restore_scene{scene} reorder_scenes{scenes} rename_scene{scene,title} target_duration{seconds,allow_speedup,max_still_seconds} background{kind,colors,padding,corner_radius,shadow} add_rule{text,effect,beats,match,value} remove_rule{id} keep_blank_frames{enabled} device_frame{mode:auto|on|off} ripple_delete{scene,start,end} shorten_range{scene,start,end,seconds} crop_scene{scene,region | fit:true | reset:true} accept_suggestions{ids | all:true} reject_suggestions{ids} reset_suggestions{ids} keystrokes{mode:off|shortcuts|all,position,size,style,hold,fade | scene,mode} (typed keys and shortcuts on screen) presenter{enabled,layout:bubble|split|full,shape,corner,position,size,border,shadow,mirror,background:none|blur|remove,quality,split_side,offset,include_audio,volume | scene,layout:hidden|bubble|split|full|inherit} (the presenter camera). All operations apply atomically (all or none) and the result carries an edit_id that edit_revert undoes. To apply a reviewed cut plan from edit_suggest_cuts, send its accepted and rejected ids in one call. Regions (crop_scene, camera, redact) are {x, y, width, height} normalized to the recorded frame: 0…1, top-left origin, not pixels (w/h are accepted for width/height). Unknown or missing keys, pixel values, empty regions and regions outside the frame are rejected with an error. crop_scene shows exactly that region of the scene, in every format and on every frame including scene cuts (letterboxed when its shape differs from the canvas); fit:true frames the scene's visible content; reset:true removes the crop. camera focus/punch with a region frames exactly that region (zoomed to fill the frame, from the scene's first frame when the directive covers the whole scene); without a region it zooms gently on the middle. Camera directives also apply to reviewer takes, whose automatic camera is off. ripple_delete removes an interior range; shorten_range keeps its first seconds and the last second before it ends. Caption clock: start/end are scene-local SOURCE seconds (as recorded, like take_frame's time), each mapped through the cuts, so after cuts a caption can start late or end early. Anchored captions don't move when the cut changes and last duration OUTPUT seconds (default 3.5), never past their scene: at:"scene_start" (the scene's first seconds, + offset), at:"checkpoint" (ends with the scene: its checkpointed final state; checkpoint_id may name the scene or a checkpoint event), event_id (starts when that event happens in the output, + offset). captions_from_intents adds one scene_start caption per scene from its intent (replace:true redoes its own). timeline_get lists where every caption lands. target_duration only speeds material up, never cuts: it avoids visible pointer movement while it can, reports exactly which source ranges it sped up and by how much (target_duration in the result), allow_speedup:false forbids speed-ups (the result then says how far over the target the cut is), and max_still_seconds first shortens every still stretch longer than that (readable, consent and result screens and stills under a caption keep their reading time). The result has a small preview frame per crop/camera/caption edit (previews, up to three inline; preview:"path" = files only, "none" = skip) and warnings when an edit has no visible effect. Blank or stalled spans (a window that stopped drawing) are cut automatically; keep_blank_frames{enabled:true} keeps them. device_frame draws a phone bezel around iOS Simulator takes (auto = 9:16 only).
Parameters
1 fields
take_id.Good to know
All operations apply atomically (all or none); times are scene-local seconds and regions are normalized 0…1 from the top-left.
{
"name": "edit_apply",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"operations": [
{
"op": "trim_scene",
"scene": "Sign in",
"head": 1.5
},
{
"op": "camera",
"scene": "Create invoice",
"mode": "punch",
"start": 12,
"end": 15.5
},
{
"op": "caption",
"scene": "Export PDF",
"start": 1,
"end": 4,
"text": "One click to a client-ready PDF"
}
]
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"status": "recorded",
"attempts": 1,
"duration": 11.8,
"checkpoint": "Invoices list visible"
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"status": "recorded",
"attempts": 2,
"duration": 24.3,
"checkpoint": "Invoice INV-0042 open with 2 line items"
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 3,
"title": "Export PDF",
"status": "recorded",
"attempts": 1,
"duration": 16.3,
"checkpoint": "PDF saved to Downloads"
}
],
"assertions_passed": "3/3",
"source_duration": 52.4,
"outputs": [
"16:9",
"9:16"
],
"edits": {
"trims": 1,
"compress_waits": {
"threshold": 1,
"to": 0.4
},
"camera_directives": 1,
"captions": 1
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ehy8a0h5tx2wqe",
"duration": 44.12
}edit_trim
Trim scene
Trim seconds from the start (head) and/or end (tail) of a scene.
Parameters
scene_id is accepted as an alias.scene.take_id.Good to know
Give head, tail or both; the response is the whole take (trimmed here) plus edit_id and the new output duration.
{
"name": "edit_trim",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene": "Sign in",
"head": 1.5,
"tail": 0.8
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"trims": 1,
"compress_waits": {
"threshold": 1,
"to": 0.4
}
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ehy8a0h5tx2wqe",
"duration": 47.36
}edit_speed
Change speed
Speed up (or slow down) part of a scene; rate 0 removes the range (a ripple delete inside the scene). start/end are seconds within the scene's recorded segment (see timeline_get).
Parameters
scene_id is accepted as an alias.scene.take_id.Good to know
start and end are seconds within the scene's recording (see timeline_get), not the finished video.
{
"name": "edit_speed",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene": "Export PDF",
"start": 4,
"end": 9.5,
"rate": 3
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"speed_ranges": 1,
"compress_waits": {
"threshold": 1,
"to": 0.4
}
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ehyf20c9qm4vtb",
"duration": 44.73
}edit_compress_waits
Compress waits
Shorten loading/wait events longer than threshold seconds to compressed_to seconds (disclosed in reviewer manifests).
Parameters
take_id.Good to know
Compression is on by default (waits over 1 s play for 0.4 s); pass enabled: false to keep waits at full length.
{
"name": "edit_compress_waits",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"threshold": 0.8,
"compressed_to": 0.3
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"compress_waits": {
"threshold": 0.8,
"to": 0.3
}
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ehyk70f2wd8nrs",
"duration": 48.9
}edit_camera
Direct camera
Override the automatic, intent-aware camera for a scene or range: wide (full window), focus (with region: frame exactly that region; without: gentle zoom on the middle), punch (tighter emphasis), none (no camera moves). A directive without start/end covers the whole scene and cuts in with it. Applies to reviewer takes too. start/end are seconds within the scene's recorded segment (see timeline_get).
Parameters
scene_id is accepted as an alias.4 fields
scene.take_id.Good to know
Omit start and end to direct the whole scene; omit region to follow the action.
{
"name": "edit_camera",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene": "Create invoice",
"mode": "focus",
"start": 6,
"end": 11,
"region": {
"x": 0.55,
"y": 0.2,
"width": 0.4,
"height": 0.35
}
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
},
"camera_directives": 1
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ehyq50n7gc1xkd",
"duration": 49.8
}edit_redact
Redact region
Blur/pixelate/cover a region in renders. Omit scene for the whole take; omit start/end for the whole scene. start/end are seconds within the scene's recorded segment (see timeline_get).
Parameters
4 fields
scene.take_id.Good to know
Omit scene to redact across the whole take, and start/end to cover the whole scene.
{
"name": "edit_redact",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"region": {
"x": 0.31,
"y": 0.26,
"width": 0.24,
"height": 0.04
},
"scene": "Create invoice",
"style": "blur",
"reason": "Customer email"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
},
"redactions": [
{
"id": "red_m3ehn3b0y5kc9rvm",
"reason": "Customer email",
"automatic": false,
"enabled": true
}
]
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ehz0t0p3bh6qws",
"duration": 49.8
}edit_pointer
Style pointer
Presentation pointer style. Motion is synthesized (minimum-jerk, target-aware), so this changes the whole take instantly.
Parameters
take_id.{
"name": "edit_pointer",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"personality": "calm",
"shape": "arrow",
"size": 1.4,
"color": "#3B82F6",
"highlight_ring": true
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
}
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ehz5m0e8vr2tjc",
"duration": 49.8
}presenter_set
Presenter overlay
Show the presenter: the camera recorded with each scene (capture_start {camera:true}) or a video imported with presenter_import. layout: bubble (circle or rounded square in a corner, or at position = the bubble's center normalized to the frame, as if dragged), split (screen and presenter side by side; stacked in 9:16) or full (the presenter fills the frame, e.g. intro/outro scenes). With scene, sets that scene's layout only (hidden leaves the presenter out of it; inherit follows the take). size small/medium/large, border none/white/accent, shadow, mirror (on by default for the recorded camera). background blur or remove uses Apple Vision person segmentation at render time on this Mac (quality fast/balanced/accurate; off by default; DemoTake Web shows the camera as recorded). The recorded camera follows cuts and speed changes exactly like the screen; an imported video plays in real time from offset output seconds (negative skips its start), with its sound (include_audio, volume).
Parameters
2 fields
corner.scene.take_id.{
"name": "presenter_set",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"layout": "bubble",
"corner": "bottom_right",
"size": "medium",
"background": "blur"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"presenter": {
"enabled": true,
"source": "camera",
"layout": "bubble",
"shape": "circle",
"corner": "bottom_right",
"size": "medium",
"border": "white",
"shadow": true,
"mirror": true,
"background": "blur",
"quality": "balanced"
}
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ei0k2s9q1d7xwf",
"duration": 49.8
}presenter_import
Import presenter video
Copy a presenter video (.mov/.mp4/.m4v, e.g. a founder narrating an agent's take afterwards) into the take and show it as the presenter, aligned with offset (the output second its first frame plays at; negative skips its start; re-align later with presenter_set {offset}). Its sound plays too unless include_audio:false. The original file is never changed. Then style it with presenter_set (layout, corner, size…).
Parameters
take_id.{
"name": "presenter_import",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"path": "/Users/sam/Movies/founder-voiceover.mov",
"offset": 1.5
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"presenter": {
"enabled": true,
"source": "imported",
"layout": "bubble",
"corner": "bottom_right",
"size": "medium",
"mirror": false,
"background": "none",
"imported": {
"file": "media/presenter-m3ej2p.mov",
"name": "founder-voiceover.mov",
"duration": 46.2,
"offset": 1.5,
"width": 1080,
"height": 1920,
"has_audio": true,
"include_audio": true,
"volume": 1
}
}
},
"next": "Edit (edit_* / edit_command), then export_render."
}keystrokes_set
Show keystrokes
Show typed keys and shortcuts on screen, drawn from the take's logged typing and key events (keyboard_type, keyboard_key, take_log_event kind type/key_press, and a person's typing in DemoTake Agent). mode: off, shortcuts (⌘/⌃/⌥ combos and ↩ ⇥ ⎋; repeats merge as "⌘Z ×3") or all (typed text too, in short bursts). Typing into secure fields, masked events and anything Secret Guard marks secret (passwords, emails, keys, codes) is never shown. With scene, sets that scene's mode only (inherit = follow the take). Renders in previews and every video export (Mac and DemoTake Web); timeline_get lists the cards.
Parameters
scene.take_id.{
"name": "keystrokes_set",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"mode": "shortcuts",
"position": "bottom_center",
"style": "dark",
"size": "medium"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
},
"keystrokes": {
"mode": "shortcuts",
"position": "bottom_center",
"size": "medium",
"style": "dark",
"hold": 1.2,
"fade": 0.2
}
},
"next": "Edit (edit_* / edit_command), then export_render.",
"edit_id": "edt_m3ei0k2s9q1d7xwe",
"duration": 49.8
}edit_command
Edit by instruction
Natural-language edit, like the editor's command bar: "make it 30 seconds", "remove the hesitation after login", "don't zoom on typing", "redo the login scene". Returns what was applied and any scenes that need re-recording.
Parameters
take_id.Good to know
Common phrasings are handled on your Mac (interpreter "local"); free-form requests need DemoTake AI ("ai"). Scenes listed in retry_scenes must be re-recorded.
{
"name": "edit_command",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"text": "make it 30 seconds"
}
}{
"applied": [
"Target length 30s"
],
"message": "Done.",
"interpreter": "local",
"retry_scenes": []
}timeline_get
Timeline
The edited timeline for an output format: scene placement in output seconds, surviving events with their edits and output times, zoom ranges, where each caption lands (and its clock), every source range played faster or removed with its cause (target_duration, suggestion, speed_range, wait_compression, event_edit, blank), what target_duration did, blank spans with what the capture saw, and keystroke cards. Use it to pick times for edits.
Parameters
take_id.Good to know
This result uses camelCase keys (takeID, outputStart…), unlike most tools; times here are output seconds, while edit tools take scene-local seconds.
{
"name": "timeline_get",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "16:9"
}
}{
"takeID": "take_m3ehbyc0k7qv2x9d",
"format": "16:9",
"duration": 41.8,
"scenes": [
{
"sceneID": "scn_m3ehbzb8a1fr6mzp",
"title": "Sign in",
"outputStart": 0,
"outputEnd": 9.6,
"sourceDuration": 11.8,
"attempts": 1,
"status": "recorded"
},
{
"sceneID": "scn_m3ehbzb9d3kw7tbe",
"title": "Create invoice",
"outputStart": 9.6,
"outputEnd": 26.9,
"sourceDuration": 24.3,
"attempts": 2,
"status": "recorded"
},
{
"sceneID": "scn_m3ehbzbap5hn0cxv",
"title": "Export PDF",
"outputStart": 26.9,
"outputEnd": 41.8,
"sourceDuration": 16.3,
"attempts": 1,
"status": "recorded"
}
],
"events": [
{
"eventID": "evt_m3ehep8g6yzk1mcd",
"sceneID": "scn_m3ehbzb9d3kw7tbe",
"outputTime": 12.35,
"kind": "click",
"beat": "interaction",
"label": "Click New Invoice",
"camera": "focus"
},
{
"eventID": "evt_m3ehes4g9rbt6kxh",
"sceneID": "scn_m3ehbzb9d3kw7tbe",
"outputTime": 13.1,
"kind": "wait",
"beat": "waiting",
"label": "Waiting for invoice editor",
"edit": {
"action": "compress",
"mask": false,
"compressTo": 0.4
}
}
],
"zooms": [
{
"start": 12.1,
"end": 15.8
}
],
"blanks": []
}edit_suggest_cuts
Suggest cuts
Review a take's footage for dead space and return a cut plan. Doesn't change the video or its edits: the plan is saved as suggestions, so the editor shows the same bands. It looks at visual change (downscaled frame differences), clicks/typing/scrolls, logged events and on-screen text. A still screen is a candidate, not dead air: each suggestion shortens a stretch to keep_seconds (its start plus a little context before the next change), readable, consent and result screens keep reading time and are flagged needs_review, and nothing is cut across assertions, checkpoints or result states (protected). Times are scene-local seconds (start/end = the still stretch, cut_start/cut_end = what would be removed). planned_duration is the output length if every pending suggestion is accepted. Apply a reviewed plan atomically with edit_apply {operations:[{op:"accept_suggestions", ids:[…]}, {op:"reject_suggestions", ids:[…]}]}; it returns an edit_id for edit_revert. Also reports framing problems per scene (mostly black frames, content in a corner) with a suggested crop (edit_apply crop_scene{scene, fit:true}). max_still_seconds N: no still longer than N seconds — every unchanged stretch longer than N + 0.5 s is shortened to N, except readable, consent and result screens and stills under a caption, which keep their reading time (flagged needs_review); accept them with accept_suggestions{all:true}, or use edit_apply target_duration{seconds, max_still_seconds} to shorten stills and reach a length in one edit. On-screen text that wasn't read cleanly is never quoted in a reason. blank_spans lists the stretches where the recording went blank (cut automatically) with their cause (window_covered, window_hidden, app_not_drawing, tab_switched, screen_locked, capture_paused, unknown), inferred + confidence for recordings made before causes were recorded (worked out from the frames and the take's events; unknown rather than a guess), the evidence and a hint for the next recording.
Parameters
scene.take_id.Good to know
Times are scene-local seconds; apply the reviewed plan in one edit_apply call with accept_suggestions and reject_suggestions.
{
"name": "edit_suggest_cuts",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"analyzed_at": "2026-09-26T10:02:11Z",
"current_duration": 72.4,
"planned_duration": 53.2,
"baseline_duration": 72.4,
"pending_seconds": 19.2,
"accepted_seconds": 0,
"suggestions": [
{
"id": "cut_m3ehv6n0e4pa9sjt",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"scene_title": "Create invoice",
"status": "pending",
"start": 8.4,
"end": 24.1,
"cut_start": 12.4,
"cut_end": 23.1,
"keep_seconds": 5,
"saved_seconds": 10.7,
"confidence": 0.92,
"kind": "still",
"content": "plain",
"needs_review": false,
"reason": "Nothing changed on screen for 15.7 s and nobody clicked or typed. Keeps 5.0 s."
},
{
"id": "cut_m3ehv6n1z7gq5wkb",
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"scene_title": "Sign in",
"status": "pending",
"start": 2.1,
"end": 18.6,
"cut_start": 9.1,
"cut_end": 17.6,
"keep_seconds": 8,
"saved_seconds": 8.5,
"confidence": 0.67,
"kind": "still",
"content": "consent",
"needs_review": true,
"reason": "Consent screen “Ledgerly wants to access your Google Account” unchanged for 16.5 s. Keeps 8.0 s so it can be read.",
"headline": "Ledgerly wants to access your Google Account",
"words": 38
}
],
"protected": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"start": 2.1,
"end": 10.1,
"kind": "consent",
"label": "Ledgerly wants to access your Google Account"
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"start": 23.35,
"end": 27.2,
"kind": "assertion",
"label": "Invoice created"
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"start": 12.3,
"end": 16.3,
"kind": "scene_end",
"label": "PDF saved to Downloads"
}
],
"framing": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"scene_title": "Sign in",
"issues": [],
"summary": "Framing looks fine.",
"black_fraction": 0.01,
"black_frame_share": 0,
"content_area": 0.98,
"content_bounds": {
"x": 0.01,
"y": 0,
"width": 0.98,
"height": 1
}
}
],
"apply": "edit_apply {take_id, operations:[{op:\"accept_suggestions\", ids:[…]}, {op:\"reject_suggestions\", ids:[…]}]}"
}take_contact_sheet
Contact sheet
A grid of frames sampled from each scene, to check framing and content before exporting (returned as an image, plus the file path). edited:true (default) shows frames as they will export (crop, background, camera) and labels them with OUTPUT times ("out 1:04.5", seconds in the edited video); edited:false shows them as recorded with scene-local SOURCE times ("rec 0:12.0"). time_base in the result says which. Scenes with framing problems found by edit_suggest_cuts are labeled. blank_segments lists spans where the recording showed nothing, with their cause (window_covered, window_hidden, app_not_drawing, tab_switched, screen_locked, capture_paused, unknown; inferred with a confidence for older recordings), the evidence and a hint.
Parameters
scene.take_id.Good to know
The image is returned as an image content block; scenes with framing problems also get a framing array with a suggested crop.
{
"name": "take_contact_sheet",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"frames_per_scene": 6
}
}{
"path": "/var/folders/…/T/DemoTake-Previews/take_m3ehbyc0k7qv2x9d/contact-sheet-all.png",
"width": 1600,
"height": 1068
}take_frame
Frame preview
One frame of a take as an image: scene + time (scene-local seconds, as recorded) or output_time (seconds in the edited output, as exported). Use it to check both sides of a cut before accepting it.
Parameters
scene_id is accepted as an alias.scene.take_id.Good to know
Give scene with time, or output_time on its own; the PNG is returned as an image content block.
{
"name": "take_frame",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene": "Create invoice",
"time": 12.5
}
}{
"path": "/var/folders/…/T/DemoTake-Previews/take_m3ehbyc0k7qv2x9d/frame-scn_m3ehbzb9d3kw7tbe-12.50.png",
"width": 1280,
"height": 800
}preview_frame
Preview frame
The whole composed frame of the edited timeline at an output time, as a PNG — what the export will show there: crop, camera, background, captions, overlays, cursor and redactions (the trial watermark is left out). Use it after edits instead of exporting: it also reports the scene and scene-local source time shown, the crop, the part of the recording in view (viewport, normalized source coordinates) and the caption. output_time is seconds in the edited output (timeline_get); aspect picks the format.
Parameters
take_id.Good to know
The PNG is returned as an image content block. output_time is seconds in the edited video (timeline_get). The trial watermark is left out.
{
"name": "preview_frame",
"arguments": {
"take_id": "take_m5tq2r8c0vk7wd3n",
"output_time": 12.5,
"aspect": "16:9"
}
}{
"path": "/var/folders/…/T/DemoTake-Previews/take_m5tq2r8c0vk7wd3n/edited-16x9-12.50.png",
"width": 1280,
"height": 720,
"format": "16:9",
"output_time": 12.5,
"duration": 54.2,
"time_base": "output",
"scene_id": "scn_m5tq2r9d4bx1kf6p",
"scene_title": "Authorize",
"scene_time": 4.8,
"viewport": {
"x": 0,
"y": 0,
"width": 1,
"height": 1
},
"caption": "TikTok asks for 2 permissions"
}preview_clip
Preview clip
A short, low-resolution MP4 of part of the edited timeline (start/end in output seconds, at most 60 s), rendered like the export (crop, camera, captions, overlays, cursor) but small and fast, into a temporary folder. Returns its path, size and frame rate. scale is the size relative to the format's canvas (0.5 = 960×540 for 16:9). Use it to check motion (camera moves, cursor, speed-ups, transitions) before exporting.
Parameters
take_id.Good to know
At most 60 seconds. scale is relative to the format's canvas (0.5 = 960×540 for 16:9).
{
"name": "preview_clip",
"arguments": {
"take_id": "take_m5tq2r8c0vk7wd3n",
"start": 10,
"end": 24,
"scale": 0.5
}
}{
"path": "/var/folders/…/T/DemoTake-Previews/take_m5tq2r8c0vk7wd3n/clip-16x9-10.0-24.0.mp4",
"format": "16:9",
"start": 10,
"end": 24,
"duration": 14,
"width": 960,
"height": 540,
"fps": 30,
"file_size_bytes": 1184322
}edit_revert
Revert edit
Revert one edit by the edit_id that edit_apply (or any edit_* tool) returned. Later edits that touched other things are kept; list recent ids with edit_history.
Parameters
take_id.{
"name": "edit_revert",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"edit_id": "edt_m3ehy8a0h5tx2wqe"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"name": "Create invoice",
"status": "verified",
"assertions_passed": "3/3",
"source_duration": 52.4,
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
}
},
"next": "Edit (edit_* / edit_command), then export_render.",
"reverted_edit_id": "edt_m3ehy8a0h5tx2wqe",
"duration": 49.8
}edit_history
Edit history
Recent edits applied through edit_apply / edit_* tools, newest first, with their edit_id (for edit_revert) and whether they were reverted.
Parameters
take_id.Good to know
source is "agent" for MCP edits, "editor" for edits made in the app and "command" for command-bar edits.
{
"name": "edit_history",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"edits": [
{
"edit_id": "edt_m3ehz5m0e8vr2tjc",
"applied_at": "2026-09-26T10:06:40Z",
"source": "agent",
"summary": [
"Pointer Calm"
]
},
{
"edit_id": "edt_m3ehy8a0h5tx2wqe",
"applied_at": "2026-09-26T10:04:12Z",
"source": "agent",
"summary": [
"Trim scene (head 1.5s, tail –)",
"Camera → punch",
"Caption “One click to a client-ready PDF”"
],
"reverted_at": "2026-09-26T10:07:03Z"
}
]
}review_check
Check for platform review
Check a reviewer take against what the platform's reviewer looks for, and get a short paste-ready checklist for the review form (checklist). Checks: the website domain the video shows (from browser-mode URLs or navigation events) matches app_url, and the address bar is visible; every declared scope has a scene in the cut that shows it (scenes[].shows_scopes); consent screens (scenes that show scopes, and consent screens edit_suggest_cuts found) stay on screen at least 3 s; and the latest export's format and size (TikTok app review: MP4 or MOV, at most 50 MB; for Meta, Google and Apple only the format is checked). Each check is pass, fail, warn or unknown with a fix. Uses the take's preset (take_create {platform, app_url, scopes}); platform/app_url/scopes passed here apply to this check only. Read-only: never changes the take (its preset, purpose, style or edits).
Parameters
take_id.Good to know
Uses the take's preset from take_create {platform, app_url, scopes}; platform, app_url and scopes passed here apply to this check only. Each check is pass, fail, warn or unknown, with a fix when it isn't a pass.
{
"name": "review_check",
"arguments": {
"take_id": "take_m5tq2r8c0vk7wd3n"
}
}{
"take_id": "take_m5tq2r8c0vk7wd3n",
"platform": "tiktok",
"passed": true,
"checks": [
{
"id": "domain",
"title": "Website domain visible",
"status": "pass",
"detail": "Shows example.com and tiktok.com in the address bar; matches the app URL."
},
{
"id": "scopes",
"title": "Every scope demonstrated",
"status": "pass",
"detail": "user.info.basic and video.upload each have a scene in the cut."
},
{
"id": "consent_readable",
"title": "Consent screens readable",
"status": "pass",
"detail": "The consent screen stays on screen for 6.2 s."
},
{
"id": "file",
"title": "Video file within the upload limits",
"status": "pass",
"detail": "MP4, 31.4 MB (limit 50 MB)."
}
],
"domains_shown": [
"example.com",
"tiktok.com"
],
"scope_scenes": {
"user.info.basic": [
"Authorize"
],
"video.upload": [
"Draft in TikTok inbox"
]
},
"export_path": "/Users/you/Movies/DemoTake/Projects/…/exports/tiktok-review-16x9-20261026-141902.mp4",
"checklist": "Demo video: tiktok-review-16x9-20261026-141902.mp4 (MP4, 31.4 MB, 0:54)\nWebsite shown: example.com (matches the app URL https://example.com)\nSteps shown:\n…"
}export_render
Render
Render outputs locally (16:9, 9:16, 4:3, 1:1, gif, screenshots, reviewer_pack, guide, interactive). Returns job ids immediately; pass wait=true to block until done (progress is reported); export_cancel stops a job. Each video export preflights its clips, reads any clip that won't decode from a compatible H.264 copy (reported in fallbacks), and validates the finished MP4 before completing; failures carry a diagnosis. quality: "high" (default; 60 fps, large files) or "upload" (H.264 at 30 fps with a bitrate tuned for app recordings: small files for upload forms and review portals). max_mb picks the bitrate so the MP4 lands under that size (MB of 1,000,000 bytes; encoding again at a lower bitrate if the first encode comes out too big). Completed jobs report file_size_bytes / file_size_mb, over_limit (with max_mb), and platform_limits (TikTok app review: MP4 or MOV, at most 50 MB); an export still over max_mb is completed with a warning. Quality and max_mb apply to the video formats; GIF, screenshots and guides ignore them. A take with the TikTok review preset exports at quality "upload", max_mb 45 unless you pass quality or max_mb. subtitles:["srt","vtt","txt","md"] writes subtitle/transcript files next to each video (timed to the cut; listed in the job's side_files); embed_subtitles:true also adds a soft subtitle track players can switch on. subtitle_source: auto (the voiceover when there is one, else the captions), captions or narration. A Reviewer Pack always carries captions.vtt/.srt (when there's something said) and transcript.md. Localized takes: languages:["source","de","sv"] (or "all") renders each format once per language — the take written in that language, its voiceover and on-screen text callouts — with the language in the file name (-de, -sv) and per-language subtitles, transcripts, Guide and Reviewer Pack; subtitle_languages:["all"] with embed_subtitles:true puts one soft subtitle track per language (tagged with its language) into each MP4. "interactive" is the click-through demo (interactive_build / interactive_export): one job for all its languages, a folder with index.html and a ZIP. resolution: "1080p" (default), "2160p" (4K: 3840×2160 for 16:9, 2160×3840 for 9:16) or "native" (the recording's own pixels, 1080p up to 2160p, never upscaled). Everything drawn (pointer, captions, keystrokes, overlays) is laid out exactly as at 1080p, with more pixels. 4K from a smaller recording is upscaled and says so in warnings. format:"app_preview" makes an App Store app preview: device iphone_6_9 (886×1920), iphone_6_5, iphone_6_3, iphone_6_1, iphone_5_8, iphone_5_5 (1080×1920), iphone_4_7 (750×1334), ipad_13 / ipad_12_9 / ipad_11 / ipad_10_5 (1200×1600), ipad_9_7 (900×1200), mac and apple_tv (1920×1080), vision_pro (3840×2160); portrait or landscape; 30 fps H.264 High Profile Level 4.0 at about 11 Mbit/s with stereo 256 kbit/s AAC (silent when the take has no sound); the app fills the frame (no backdrop or device frame). Apple accepts 15–30 s: fit "speed" speeds a long cut up (target_duration) or holds the last frame of a short one, "trim" ends a long one at 29.5 s. A poster frame PNG (poster_time, default 5 s) is taken from the finished video, and app_preview in the job reports the check against Apple's specification (problems, warnings such as prices in on-screen text, the poster time code). app_preview_upload sends it to App Store Connect. codec: automatic (HEVC for high-quality exports above 1080p, H.264 otherwise), "h264" (most compatible; 4K60 needs H.264 level 5.2) or "hevc". max_mb still caps the file at any resolution. export_preflight {resolution} estimates the size first.
Parameters
formats), e.g. "app_preview".take_id.Good to know
Without wait the jobs come back queued; poll export_status. Formats default to the take's outputs; a failed job carries a diagnosis. quality "upload" makes small H.264 files for upload forms, and max_mb keeps each MP4 under a size: jobs then report file_size_mb and over_limit. A take with the TikTok review preset exports at quality "upload", max_mb 45 unless you pass either.
{
"name": "export_render",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"formats": [
"16:9",
"9:16"
],
"wait": true
}
}{
"jobs": [
{
"job_id": "exp_m3ej19z0b8mc3rkf",
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "16:9",
"status": "completed",
"progress": 1,
"output_path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ehbyc0k7qv2x9d.take/exports/create-invoice-16x9-20260926-101204.mp4",
"validation": {
"ok": true,
"path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ehbyc0k7qv2x9d.take/exports/create-invoice-16x9-20260926-101204.mp4",
"duration": 41.8,
"expected_duration": 41.8,
"width": 1920,
"height": 1080,
"expected_width": 1920,
"expected_height": 1080,
"frames_decoded": [
0,
20.9,
41.77
]
},
"duration": 41.8,
"sha256": "3b7e5a0c9d14f2e86a1b47c3d9e0f5a2b6c8d1e4f7a0b3c6d9e2f5a8b1c4d7e0",
"completed_at": "2026-09-26T10:12:31Z"
},
{
"job_id": "exp_m3ej19z1q2dz7vhn",
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "9:16",
"status": "completed",
"progress": 1,
"output_path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ehbyc0k7qv2x9d.take/exports/create-invoice-9x16-20260926-101204.mp4",
"duration": 41.8,
"sha256": "9a41c0e7b2d85f36e1a9c4b07d2e8f5a3c6b9d0e1f4a7b2c5d8e0f3a6b9c2d5e",
"completed_at": "2026-09-26T10:12:58Z"
}
]
}export_preflight
Export preflight
Read-only check before exporting: decodes the first, middle and last frame of every clip range the cut uses (through the same pipeline export_render uses) and returns one result per clip: scene, clip file, codec, the ranges used, the frames sampled and, for a clip that fails, a diagnosis (source/output time, error domain/code, underlying error). export_render runs this automatically and reads a failing clip from a compatible H.264 copy. For video formats it also estimates the export (estimate: width, height, fps, codec, bitrate, estimated_mb, frame memory, and notes such as 4K upscaling a smaller recording) for the resolution, quality, max_mb and codec given.
Parameters
take_id.Good to know
export_render runs this check itself; a clip that fails is read from a compatible H.264 copy and reported in fallbacks.
{
"name": "export_preflight",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "16:9"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "16:9",
"ok": true,
"duration": 41.8,
"clips": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"scene_title": "Sign in",
"scene_number": 1,
"segment_id": "seg_m3ehdrz0w2jq8rts",
"clip_file": "media/seg_m3ehdrz0w2jq8rts.mov",
"codec": "HEVC (hvc1)",
"pixel_width": 2560,
"pixel_height": 1600,
"status": "ok",
"used_ranges": [
{
"start": 1.5,
"end": 11.8
}
],
"samples": [
{
"output_time": 0,
"source_time": 1.5,
"ok": true
},
{
"output_time": 4.8,
"source_time": 6.65,
"ok": true
},
{
"output_time": 9.58,
"source_time": 11.78,
"ok": true
}
]
}
]
}export_validate
Validate export
Check a finished video (job_id from export_render, or an MP4/MOV path): it opens, has a video track, its duration matches the edit plan (± a frame) and its size the format, and its first, middle and last frames decode. Reports the file size; with max_mb a bigger file is invalid ("over limit"), and files over a known platform limit (TikTok app review: MP4/MOV ≤ 50 MB) get a warning. export_render already does this before reporting a job completed.
Parameters
job_id.Good to know
Give job_id or an absolute path to an MP4/MOV; export_render already runs this before it marks a job completed.
{
"name": "export_validate",
"arguments": {
"job_id": "exp_m3ej19z0b8mc3rkf"
}
}{
"ok": true,
"path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ehbyc0k7qv2x9d.take/exports/create-invoice-16x9-20260926-101204.mp4",
"duration": 41.8,
"expected_duration": 41.8,
"width": 1920,
"height": 1080,
"expected_width": 1920,
"expected_height": 1080,
"frames_decoded": [
0,
20.9,
41.77
]
}app_preview_check
Check app preview
Check a video against Apple's App Store app preview specification for a display family: accepted size, 15–30 s, at most 30 fps, H.264 up to High Profile Level 4.0 (or ProRes 422 HQ in a MOV), stereo AAC at 44.1 or 48 kHz, MOV/M4V/MP4, at most 500 MB. Give an app_preview export's job_id (its device is known) or a path with a device. Returns ok, problems (what App Store Connect would reject) and warnings, plus the App Store Connect display type (preview_type).
Parameters
job_id.{
"name": "app_preview_check",
"arguments": {
"job_id": "exp_m3ej2b8c0d1f4g5h"
}
}{
"device": "iphone_6_9",
"device_name": "iPhone 6.9\"",
"preview_type": "IPHONE_67",
"orientation": "portrait",
"ok": true,
"problems": [],
"poster_time": 5,
"poster_time_code": "00:00:05:00",
"poster_path": "/Users/you/Movies/DemoTake/Library/Ledgerly.take/exports/create-invoice-app_preview-20261027-1012-poster.png",
"fitted": "Sped up from 41.3 s to 29.5 s to fit App Store's 30 s."
}app_preview_upload
Upload app preview
Upload a finished app_preview export to App Store Connect with the user's own API key (set up in DemoTake Settings › App Store Connect; the key stays in this Mac's Keychain and is never sent to DemoTake). It finds the app (bundle_id), its App Store version (version, in Prepare for Submission) and the locale's localization (created if missing), the preview set for the export's display type (created if missing; at most three previews each), reserves the preview, uploads it, and commits it with the poster frame time code. Apple then processes the video for up to 24 hours. Refuses a preview that fails app_preview_check unless force:true.
Parameters
job_id.{
"name": "app_preview_upload",
"arguments": {
"job_id": "exp_m3ej2b8c0d1f4g5h",
"bundle_id": "com.example.ledgerly",
"version": "2.3",
"locale": "en-US"
}
}{
"bundle_id": "com.example.ledgerly",
"version": "2.3",
"locale": "en-US",
"preview_type": "IPHONE_67",
"app_preview_id": "a1b2c3d4-0000-1111-2222-333344445555",
"app_preview_set_id": "f0e1d2c3-4444-5555-6666-777788889999",
"state": "UPLOAD_COMPLETE",
"bytes": 40812233,
"uploaded_at": "2026-10-27T10:14:02Z"
}export_status
Export status
Status of one render job (job_id), or all exports of a take (take_id) with output paths. A failed job carries diagnosis (scene, clip file, source/output time, codec, error domain/code, underlying error, whether a compatible copy was tried, and copyable details); a completed one lists fallbacks (clips read from a compatible copy) and its validation.
Parameters
take_id.job_id.Good to know
With take_id instead of job_id you get {exports: [...], requested_outputs: [...]}; status is queued, rendering, completed, failed or cancelled.
{
"name": "export_status",
"arguments": {
"job_id": "exp_m3ej19z0b8mc3rkf"
}
}{
"job_id": "exp_m3ej19z0b8mc3rkf",
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "16:9",
"status": "rendering",
"progress": 0.46,
"phase": "rendering"
}export_manifest
Proof manifest
Verified Take manifest: app version/build, macOS version, scenes and attempts, assertion results with timestamps, video SHA-256, source commit and an honest list of edits applied. Suitable for App Store reviewers.
Parameters
take_id.Good to know
The manifest uses camelCase keys; the video hash comes from the latest completed 16:9 export.
{
"name": "export_manifest",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"takeID": "take_m3ehbyc0k7qv2x9d",
"flow": "Create an invoice for a new customer and export it as PDF.",
"appName": "Ledgerly",
"bundleID": "com.example.Ledgerly",
"appVersion": "2.3.1",
"buildNumber": "418",
"macOSVersion": "Version 26.0 (Build 25A354)",
"timestamp": "2026-09-26T10:14:03Z",
"assertionsPassed": 3,
"assertionsTotal": 3,
"assertions": [
{
"title": "Signed in",
"status": "passed",
"takeTime": 10.9
},
{
"title": "Invoice created",
"status": "passed",
"takeTime": 34.62
},
{
"title": "Flow completed successfully",
"status": "passed"
}
],
"scenes": [
{
"title": "Sign in",
"status": "recorded",
"attempts": 1
},
{
"title": "Create invoice",
"status": "recorded",
"attempts": 2
},
{
"title": "Export PDF",
"status": "recorded",
"attempts": 1
}
],
"sourceCommit": "9f3c2e1a7b",
"videoSHA256": "3b7e5a0c9d14f2e86a1b47c3d9e0f5a2b6c8d1e4f7a0b3c6d9e2f5a8b1c4d7e0",
"videoFile": "create-invoice-16x9-20260926-101204.mp4",
"demotakeVersion": "1.0.0",
"edition": "agent",
"editDisclosures": [
"Waits over 1.0 s shortened to 0.4 s (still visible).",
"Scene heads/tails trimmed.",
"Some scenes were re-recorded; each scene is a single continuous take.",
"Pointer is a presentation layer synthesized from recorded actions."
]
}spec_validate
Validate demotake.json
Check a demotake.json spec (demo as code) and list errors/warnings with JSON paths. Pure check; nothing runs.
Parameters
demotake schema / spec_validate for the format.spec).Good to know
Pass the spec inline as spec (object or JSON text) or point path at a file DemoTake can read.
{
"name": "spec_validate",
"arguments": {
"spec": {
"version": 1,
"target": {
"bundle_id": "com.example.Ledgerly"
},
"takes": [
{
"name": "Create invoice",
"scenes": [
{
"title": "Sign in",
"steps": [
{
"action": "type",
"secret_env": "DEMO_PASSWORD",
"element": {
"identifier": "password-field"
}
},
{
"action": "key",
"keys": "return"
},
{
"action": "assert_text",
"text": "Invoices"
}
]
},
{
"title": "Create invoice",
"steps": [
{
"action": "click",
"element": {
"role": "button",
"title": "New Invoice"
}
},
{
"action": "type",
"text": "Northwind Studio",
"element": {
"identifier": "customer-field"
}
},
{
"action": "assert_text",
"text": "INV-0042"
}
]
}
]
}
]
}
}
}{
"valid": true,
"errors": [],
"warnings": [],
"takes": [
{
"name": "Create invoice",
"scenes": 2,
"steps": 6
}
],
"secret_env": [
"DEMO_PASSWORD"
]
}spec_run
Run demotake.json
Run a demotake.json spec: DemoTake operates the app scene by scene (Rehearse → Take), checkpoints each scene, retries only a failing scene, verifies assert_* steps and (with record=true) records and renders the outputs. record=false is a DemoTest run (no video). only_changed=true regenerates only scenes whose UI changed vs the last recording. Returns per-scene pass/fail with evidence. Long-running: send a progress token to stream progress.
Parameters
demotake schema / spec_validate for the format.spec).take.project_id.Good to know
Long-running: send a progress token to stream scene-by-scene progress; secrets are used for this run only and never stored.
{
"name": "spec_run",
"arguments": {
"path": "/Users/you/Code/ledgerly/demotake.json",
"only_changed": true,
"outputs": [
"16:9"
],
"secrets": {
"DEMO_PASSWORD": "…"
},
"source_commit": "a41d9e7"
}
}{
"passed": true,
"takes": [
{
"name": "Create invoice",
"take_id": "take_m3ekc4r0n6vd8pqa",
"passed": true,
"recorded": true,
"rehearsed": false,
"scenes": [
{
"title": "Sign in",
"scene_id": "scn_m3ekc4r1b2hx7jmt",
"status": "passed",
"attempts": 0,
"duration": 2.1,
"failures": [],
"assertions_passed": 1,
"assertions_total": 1,
"reused": true
},
{
"title": "Create invoice",
"scene_id": "scn_m3ekc4r2w9dq3fzn",
"status": "passed",
"attempts": 1,
"duration": 27.4,
"failures": [],
"assertions_passed": 1,
"assertions_total": 1,
"reused": false
},
{
"title": "Export PDF",
"scene_id": "scn_m3ekc4r3k5vp8tgc",
"status": "passed",
"attempts": 0,
"duration": 1.8,
"failures": [],
"assertions_passed": 1,
"assertions_total": 1,
"reused": true
}
],
"duration": 58.3,
"exports": [
{
"id": "exp_m3ekd7s0t1mr4hwb",
"takeID": "take_m3ekc4r0n6vd8pqa",
"format": "16:9",
"status": "completed",
"progress": 1,
"outputPath": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_m119ecc0t4w8nq2h/Takes/create-invoice--take_m3ekc4r0n6vd8pqa.take/exports/create-invoice-16x9-20260926-110842.mp4",
"createdAt": "2026-09-26T11:08:13Z",
"completedAt": "2026-09-26T11:08:42Z",
"duration": 42.6
}
],
"diff": {
"take_id": "take_m3ekc4r0n6vd8pqa",
"baseline_take_id": "take_m3ehbyc0k7qv2x9d",
"scenes": [
{
"title": "Sign in",
"status": "unchanged",
"score": 0.003,
"reasons": []
},
{
"title": "Create invoice",
"status": "changed",
"score": 0.31,
"reasons": [
"6.4% of the checkpoint snapshot changed"
]
},
{
"title": "Export PDF",
"status": "unchanged",
"score": 0.002,
"reasons": []
}
]
},
"baseline_take_id": "take_m3ehbyc0k7qv2x9d"
}
],
"started_at": "2026-09-26T11:07:10Z",
"duration": 92.5,
"record": true,
"spec_path": "/Users/you/Code/ledgerly/demotake.json"
}take_replay
Replay a take
Re-perform a recorded take's steps (its demotake spec, or steps derived from its trace). record=true re-records it in place (only_changed keeps unchanged scenes' footage); record=false runs it as a DemoTest.
Parameters
take_id.project_id.Good to know
Steps come from the take's demotake spec, or are derived from its trace when it has none.
{
"name": "take_replay",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"record": false
}
}{
"passed": true,
"takes": [
{
"name": "Create invoice",
"take_id": "take_m3ehbyc0k7qv2x9d",
"passed": true,
"recorded": false,
"rehearsed": false,
"scenes": [
{
"title": "Sign in",
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"status": "passed",
"attempts": 1,
"duration": 9.4,
"failures": [],
"assertions_passed": 1,
"assertions_total": 1,
"reused": false
},
{
"title": "Create invoice",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"status": "passed",
"attempts": 1,
"duration": 18.2,
"failures": [],
"assertions_passed": 1,
"assertions_total": 1,
"reused": false
},
{
"title": "Export PDF",
"scene_id": "scn_m3ehbzbap5hn0cxv",
"status": "passed",
"attempts": 1,
"duration": 10.6,
"failures": [],
"assertions_passed": 1,
"assertions_total": 1,
"reused": false
}
],
"duration": 38.2,
"exports": []
}
],
"started_at": "2026-09-26T11:03:10Z",
"duration": 38.9,
"record": false
}take_diff
Demo Diff
Which scenes' UI changed between a take and a baseline (accessibility state + perceptual snapshot diff of each scene checkpoint, redactions ignored). Use it to decide what to re-record after a release.
Parameters
take_id.Good to know
A scene without checkpoint evidence on both sides is unknown and treated as changed.
{
"name": "take_diff",
"arguments": {
"take_id": "take_m3ekc4r0n6vd8pqa",
"baseline_take_id": "take_m3ehbyc0k7qv2x9d"
}
}{
"take_id": "take_m3ekc4r0n6vd8pqa",
"baseline_take_id": "take_m3ehbyc0k7qv2x9d",
"scenes": [
{
"title": "Sign in",
"scene_id": "scn_m3ekc4r1b2hx7jmt",
"baseline_scene_id": "scn_m3ehbzb8a1fr6mzp",
"status": "unchanged",
"score": 0.003,
"text_score": 0,
"visual_score": 0.003,
"reasons": []
},
{
"title": "Create invoice",
"scene_id": "scn_m3ekc4r2w9dq3fzn",
"baseline_scene_id": "scn_m3ehbzb9d3kw7tbe",
"status": "changed",
"score": 0.31,
"text_score": 0.31,
"visual_score": 0.064,
"reasons": [
"+ button \"Duplicate\"",
"6.4% of the checkpoint snapshot changed"
]
},
{
"title": "Export PDF",
"scene_id": "scn_m3ekc4r3k5vp8tgc",
"baseline_scene_id": "scn_m3ehbzbap5hn0cxv",
"status": "unchanged",
"score": 0.002,
"text_score": 0,
"visual_score": 0.002,
"reasons": []
}
]
}take_to_spec
Save take as code
Convert a take into a demotake.json spec (steps with element queries, assertions, style and edit presets). Secrets become secret_env references. Commit it to the repo and run it with demotake run/test.
Parameters
take_id.Good to know
Omit path to get the spec back without writing a file; the folder must be writable by DemoTake.
{
"name": "take_to_spec",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"path": "/Users/you/Code/ledgerly/demotake.json"
}
}{
"spec": {
"$schema": "https://demotake.app/schema/demotake-1.json",
"version": 1,
"target": {
"bundle_id": "com.example.Ledgerly",
"app_name": "Ledgerly"
},
"takes": [
{
"name": "Create invoice",
"goal": "Create an invoice for a new customer and export it as PDF.",
"outputs": [
"16:9",
"9:16"
],
"scenes": [
{
"title": "Sign in",
"intent": "Sign in with the demo account",
"checkpoint": "Invoices list visible",
"steps": [
{
"action": "type",
"secret_env": "DEMO_PASSWORD",
"element": {
"identifier": "password-field"
}
},
{
"action": "key",
"keys": "return"
},
{
"action": "assert_text",
"text": "Invoices"
}
]
},
{
"title": "Create invoice",
"checkpoint": "Invoice INV-0042 open with 2 line items",
"steps": [
{
"action": "click",
"element": {
"role": "button",
"title": "New Invoice"
}
},
{
"action": "type",
"text": "Northwind Studio",
"element": {
"identifier": "customer-field"
}
}
]
}
]
}
]
},
"path": "/Users/you/Code/ledgerly/demotake.json"
}take_export_package
Export take package
Zip a take as a portable .take package (take.json, media, pointer tracks, snapshots; exports optional) for sharing, the Team Library or another Mac.
Parameters
take_id.Good to know
Stop any running capture on the take first; rendered exports are left out unless include_exports is true.
{
"name": "take_export_package",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"include_exports": false
}
}{
"path": "/Users/you/Movies/DemoTake/Packages/create-invoice--take_m3ehbyc0k7qv2x9d.take.zip",
"bytes": 48213504
}take_import_package
Import take package
Import a .take package (folder or .zip, e.g. from the Web Recorder or a teammate) into the library. A colliding take id gets a fresh one.
Parameters
project_id.Good to know
Without project_id the take goes to My Demos; a take id that already exists in the library gets a fresh one.
{
"name": "take_import_package",
"arguments": {
"path": "/Users/you/Downloads/create-invoice--take_m3ehbyc0k7qv2x9d.take.zip",
"project_id": "prj_m119ecc0t4w8nq2h"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"project_id": "prj_m119ecc0t4w8nq2h",
"name": "Create invoice",
"status": "verified",
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"status": "recorded",
"attempts": 1,
"duration": 11.8
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"status": "recorded",
"attempts": 2,
"duration": 24.3
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 3,
"title": "Export PDF",
"status": "recorded",
"attempts": 1,
"duration": 16.3
}
],
"assertions_passed": "3/3",
"source_duration": 52.4,
"next": "Edit (edit_* / edit_command), then export_render."
}screenshot_formats
Screenshot formats
Store and web screenshot sizes DemoTake renders exactly: App Store (iPhone 6.9″/6.5″/6.3″/6.1″, iPad 13″, Mac, Apple TV, Vision Pro), Google Play (phone, 7″ and 10″ tablet, Chromebook, feature graphic) and web (16:9, Open Graph 1200 × 630, square). Each has an id, pixel size, default device frame and the store page it comes from.
Parameters
Good to know
Store formats always export without transparency, because the App Store and Google Play reject images with an alpha channel.
{
"name": "screenshot_formats",
"arguments": {
"store": "google_play"
}
}{
"formats": [
{
"id": "play-phone",
"store": "google_play",
"name": "Phone, portrait (9:16)",
"width": 1080,
"height": 1920,
"device_class": "phone",
"default_frame": "phone",
"required": false,
"folder": "google-play/phone",
"source": "https://support.google.com/googleplay/android-developer/answer/9866151"
},
{
"id": "play-tablet-10",
"store": "google_play",
"name": "10-inch tablet (16:9)",
"width": 2560,
"height": 1440,
"device_class": "tablet",
"default_frame": "tablet",
"required": false,
"folder": "google-play/tablet-10",
"source": "https://support.google.com/googleplay/android-developer/answer/9866151"
}
],
"aliases": {
"iphone": [
"appstore-iphone-6.9"
],
"ipad": [
"appstore-ipad-13"
],
"play": [
"play-phone"
],
"og": [
"web-og"
]
}
}screenshot_set_create
Create screenshot set
Create a Screenshot Set: the captures (shots) of one app, their headlines, a style and the output formats. Stored in the library like takes. Then capture screens into it with screenshot_capture.
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
project_id.{
"name": "screenshot_set_create",
"arguments": {
"name": "Ledgerly App Store",
"formats": [
"iphone",
"ipad",
"play",
"og"
],
"background": "midnight",
"device_frame": "auto",
"typography": "modern"
}
}{
"set_id": "shots_01j9w3k2m4n5p6q7",
"name": "Ledgerly App Store",
"project_id": "prj_01j8k2v5t9x3r1m7",
"formats": [
{
"id": "appstore-iphone-6.9",
"name": "iPhone 6.9″ display",
"size": "1320 × 2868"
},
{
"id": "appstore-ipad-13",
"name": "iPad 13″ display",
"size": "2064 × 2752"
},
{
"id": "play-phone",
"name": "Phone, portrait (9:16)",
"size": "1080 × 1920"
},
{
"id": "web-og",
"name": "Open Graph / link preview",
"size": "1200 × 630"
}
],
"style": {
"background": "midnight",
"device_frame": "auto",
"frame_tone": "graphite",
"typography": "modern",
"text_color": "auto",
"text_position": "top",
"fit": "smart",
"shadow": true
},
"shots": [],
"next": "screenshot_capture {set_id, target, shot} for each screen you want."
}screenshot_set_get
Get screenshot set
A Screenshot Set's shots (capture paths, sizes, headlines, redactions with ids, and what Secret Guard blurred: capture.secret_guard.regions with id, kind and whether it's blurred), style and formats. Without set_id or set_name, lists the sets in the library.
Parameters
set_id.project_id.{
"name": "screenshot_set_get",
"arguments": {
"set_id": "shots_01j9w3k2m4n5p6q7"
}
}{
"set_id": "shots_01j9w3k2m4n5p6q7",
"name": "Ledgerly App Store",
"shots": [
{
"shot_id": "shot_01j9w3m8r2t4v6x8",
"name": "Invoices",
"headline": "Every invoice at a glance",
"captured": true,
"capture": {
"path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_01j8k2v5t9x3r1m7/Screenshots/ledgerly-app-store--shots_01j9w3k2m4n5p6q7.shots/captures/cap_01j9w3m8.png",
"width": 2560,
"height": 1600,
"scale": 2,
"source": "Ledgerly — Invoices",
"secret_regions_blurred": 1
},
"redactions": []
}
]
}screenshot_capture
Capture a screenshot
Capture one screen of the app into a Screenshot Set at native (Retina) resolution: a window or app (covered windows are captured where macOS allows), an iOS Simulator device (target "sim:<UDID>", DemoTake Agent), an Android emulator or device (target "android:<serial>", DemoTake Agent), a whole display, or a rectangle of the screen in points. Secret Guard blurs secrets it reads before the image is saved (redact:false turns that off; regions turned off with screenshot_compose's secret_guard.ignore stay off when the shot is captured again); redact_regions hides more at render time. Reusing a shot name replaces that shot's capture and keeps its words. DemoTake Agent can reach the screen first with steps (the demotake.json step vocabulary), e.g. steps:[{"click":"Settings"},{"wait":"Preferences"}]. Returns the capture and a small preview image.
steps, so "go to settings and capture it" is one call.Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
display when given, else the display it's on.4 fields
shot (a take's scene title).4 fields
steps (this call only).scene.target.12 fields
set_id.project_id.shot.Good to know
Reusing a shot name replaces its image and keeps its headline and hidden regions. Pass display for a whole display, or rect {x, y, width, height} in screen points for an area. Steps need DemoTake Agent; in DemoTake Studio, open the screen with your own tools first.
{
"name": "screenshot_capture",
"arguments": {
"set_id": "shots_01j9w3k2m4n5p6q7",
"target": "window:4127",
"shot": "Settings",
"headline": "Reminders your way",
"steps": [
{
"key": "cmd+,"
},
{
"wait": "General"
}
]
}
}{
"set_id": "shots_01j9w3k2m4n5p6q7",
"shot": {
"shot_id": "shot_01j9w3q1s3u5w7y9",
"name": "Settings",
"headline": "Reminders your way",
"captured": true,
"capture": {
"width": 2560,
"height": 1600,
"scale": 2,
"source": "Ledgerly — Settings",
"secret_regions_blurred": 0
}
},
"path": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_01j8k2v5t9x3r1m7/Screenshots/ledgerly-app-store--shots_01j9w3k2m4n5p6q7.shots/captures/cap_01j9w3q1.png",
"created": true,
"secret_regions_blurred": 0,
"shots_in_set": 2
}screenshot_compose
Compose screenshots
Set how a Screenshot Set's shots are rendered: formats, background, device frame (generic phone, tablet, laptop or window), headline typography and per-shot headlines, subheadlines and redactions (add regions, remove them by id, or turn off a Secret Guard blur the detector got wrong with secret_guard:{ignore:["sg2"]}). Saves the set and returns downscaled previews of every shot × format (the first as an image) so you can check the result before screenshot_export.
Parameters
11 fields
4 fields
2 fields
set_id.Good to know
Frames are generic phone, tablet, laptop and window shapes drawn by DemoTake. device_frame: auto picks the right one per format.
{
"name": "screenshot_compose",
"arguments": {
"set_id": "shots_01j9w3k2m4n5p6q7",
"background": "ocean",
"typography": "rounded",
"shots": [
{
"shot": "Invoices",
"headline": "Every invoice at a glance",
"subheadline": "Paid, due and overdue on one screen."
},
{
"shot": "Settings",
"redact_regions": [
{
"x": 0.62,
"y": 0.08,
"width": 0.3,
"height": 0.05
}
]
}
],
"order": [
"Invoices",
"Settings"
]
}
}{
"set_id": "shots_01j9w3k2m4n5p6q7",
"previews": [
{
"shot_id": "shot_01j9w3m8r2t4v6x8",
"shot": "Invoices",
"format": "appstore-iphone-6.9",
"width": 295,
"height": 640,
"path": "/Users/you/…/previews/invoices--appstore-iphone-6.9.png"
}
]
}screenshot_export
Export screenshots
Render every shot × format of a Screenshot Set at its exact store size into folders per store and device (app-store/iphone-6.9/01-dashboard.png …) with a manifest.json (sizes, SHA-256). PNG by default (no alpha for store formats, as the stores require), or JPEG. Identical inputs give identical files.
Parameters
set_id.Good to know
Pass file_type: "jpeg" for JPEG, formats or shots to export part of the set, and out_dir to also copy the files somewhere DemoTake can write.
{
"name": "screenshot_export",
"arguments": {
"set_id": "shots_01j9w3k2m4n5p6q7"
}
}{
"set_id": "shots_01j9w3k2m4n5p6q7",
"root": "/Users/you/Movies/DemoTake/Projects/ledgerly--prj_01j8k2v5t9x3r1m7/Screenshots/ledgerly-app-store--shots_01j9w3k2m4n5p6q7.shots/exports",
"rendered": 8,
"reused": 0,
"manifest": {
"version": 1,
"generator": "DemoTake",
"set": "Ledgerly App Store",
"files": [
{
"path": "app-store/iphone-6.9/01-invoices.png",
"format": "appstore-iphone-6.9",
"shot": "Invoices",
"width": 1320,
"height": 2868,
"bytes": 1482311,
"sha256": "4b8e…",
"headline": "Every invoice at a glance"
}
],
"warnings": []
}
}screenshot_run
Run screenshots as code
Run the screenshots section of a demotake.json: for each shot, reach the screen with its steps (DemoTake Agent), capture it, compare it with the previous capture and render every format. only_changed:true keeps shots whose screen didn't change (same files, byte for byte). Returns per-shot pass/fail with diffs and the export manifest. Specs without steps also run in DemoTake Studio. This is what demotake screenshots run calls.
screenshots section of a demotake.json: it reaches each screen with the shot's steps, captures it, compares it with the last capture and renders every format. With only_changed, screens that didn't change keep their files byte for byte. This is what demotake screenshots run calls in CI.Parameters
demotake schema / spec_validate for the format.spec).project_id.Good to know
Shots with steps need DemoTake Agent. A failing shot fails the run and nothing is exported, so a broken build never overwrites good screenshots.
{
"name": "screenshot_run",
"arguments": {
"path": "/Users/you/code/ledgerly/demotake.json",
"only_changed": true
}
}{
"set_id": "shots_01j9w3k2m4n5p6q7",
"run": {
"passed": true,
"takes": [
{
"name": "Ledgerly store screenshots",
"take_id": "shots_01j9w3k2m4n5p6q7",
"passed": true,
"scenes": [
{
"title": "Invoices",
"status": "passed",
"reused": true,
"diff": {
"title": "Invoices",
"status": "unchanged",
"score": 0.002
}
},
{
"title": "Settings",
"status": "passed",
"reused": false,
"diff": {
"title": "Settings",
"status": "changed",
"score": 0.14,
"reasons": [
"14.0% of the checkpoint snapshot changed"
]
}
}
]
}
]
},
"export": {
"rendered": 4,
"reused": 4
}
}overlay_templates
Overlay templates
Built-in overlay templates (study/pomodoro timer, progress bar, lower third, title, callout, animated chart, number counter, spotlight, keyboard shortcut, confetti, end card) with their parameters and defaults, plus the authoring rules for custom HTML/CSS and 3D scenes (variables, bindings, limits). Read before overlay_add.
Parameters
Good to know
guide:true (the default) adds the authoring guide: CSS variables (--t, --p, --scene…), data-bind bindings and formats, sanitizer rules and the 3D scene schema.
{
"name": "overlay_templates",
"arguments": {
"guide": false
}
}{
"templates": [
{
"id": "pomodoro-timer",
"name": "Study timer",
"category": "Timers",
"aliases": [
"pomodoro",
"timer",
"study-timer",
"study-with-me"
],
"default_placement": {
"anchor": "top-right",
"size": 0.26,
"aspect": 1.55,
"margin": 0.035
},
"params": [
{
"name": "focusMinutes",
"label": "Focus",
"kind": "number",
"default": 25,
"min": 1,
"max": 240
},
{
"name": "breakMinutes",
"label": "Break",
"kind": "number",
"default": 5,
"min": 0,
"max": 120
},
{
"name": "style",
"label": "Style",
"kind": "choice",
"default": "glass",
"options": [
"minimal",
"glass",
"bold"
]
}
]
}
]
}overlay_add
Add overlay
Add an effects layer over the video: a built-in template with params (see overlay_templates), or custom html + css (sanitized: no scripts, no remote URLs; time via CSS variables --t/--p and data-bind bindings; animations are seeked frame-exactly), with timing (whole video, a scene, a range or an event) and placement. The overlay is validated and a test frame is rendered (blank or broken output is rejected); it's an ordinary edit (edit_id, edit_revert). 3D scenes: scene3d_create.
Parameters
5 fields
6 fields
4 fields
take_id.Good to know
Custom overlays: pass html and css instead of template. Timing: {start, end} in output seconds, {scene: "3"} for a scene, or {event: "Publish", duration: 3}. Scripts, event handlers and remote URLs are removed.
{
"name": "overlay_add",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"template": "pomodoro-timer",
"params": {
"focusMinutes": 50,
"breakMinutes": 10,
"label": "Chapter 3 notes"
},
"placement": {
"anchor": "top-right"
}
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay": {
"id": "ovl_m3ek2c7h9d4pq1za",
"name": "Study timer",
"source": {
"kind": "template",
"template": "pomodoro-timer",
"params": {
"focusMinutes": 50,
"breakMinutes": 10,
"label": "Chapter 3 notes"
}
},
"timing": {},
"placement": {
"anchor": "top-right",
"size": 0.28,
"aspect": 1.6,
"margin": 0.04
},
"layer": "top",
"z": 0,
"opacity": 1,
"enabled": true,
"seed": 1,
"output_range": {
"start": 0,
"end": 94.2
},
"pixel_rect_16x9": {
"x": 1156,
"y": 43,
"width": 484,
"height": 302
}
},
"edit_id": "edt_m3ek2c7j0v6b3n8w"
}overlay_update
Update overlay
Change an overlay with a JSON merge patch of its fields: {"params":{…}} (template/custom params), {"html","css"}, {"timing":{…}}, {"placement":{…}}, {"layer"}, {"z"}, {"opacity"}, {"name"}, {"enabled":false} to hide it. Re-validated and render-checked; returns an edit_id.
Parameters
5 fields
6 fields
4 fields
take_id.overlay_id.Good to know
enabled:false hides an overlay without removing it.
{
"name": "overlay_update",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek2c7h9d4pq1za",
"params": {
"style": "bold"
},
"timing": {
"scene": "Deep work"
}
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay": {
"id": "ovl_m3ek2c7h9d4pq1za",
"name": "Study timer",
"timing": {
"scene": "scn_m3ehbzb9d3kw7tbe"
}
},
"edit_id": "edt_m3ek2d1q8r2m5t0c"
}overlay_remove
Remove overlay
Remove an overlay (or a whole 3D scene). Returns an edit_id that edit_revert undoes.
Parameters
take_id.overlay_id.{
"name": "overlay_remove",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek2c7h9d4pq1za"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlays": [],
"edit_id": "edt_m3ek2e5x3y7k1p9d"
}overlay_preview
Preview overlay
PNG of a frame with overlays, rendered exactly as the export will (same runtime, same time). Give overlay_id to center on that overlay (isolated:true renders it alone on transparency), and/or output_time.
Parameters
take_id.overlay_id.Good to know
isolated:true renders the overlay alone on a transparent background.
{
"name": "overlay_preview",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek2c7h9d4pq1za",
"output_time": 61.5
}
}{
"path": "/var/folders/…/T/DemoTake-Previews/take_m3ehbyc0k7qv2x9d/overlays-61.50.png",
"width": 1280,
"height": 720
}scene3d_create
Create 3D scene
Add a 3D overlay rendered by DemoTake's bundled engine (deterministic, frame-exact; data, never code). Start from a preset (spinning-logo {text}, floating-screen — the recording on a tilted screen with a dolly-in, bar-columns, snow, sparkles, confetti, dust, bubbles, orbit-text) or pass a full scene (schema in overlay_templates' guide: objects, PBR materials, lights, camera, keyframe tracks, particles, recording planes, embedded .glb models/images). Validated against budgets and render-checked. Refine with scene3d_add_object / scene3d_update / scene3d_animate.
Parameters
5 fields
6 fields
4 fields
scene.take_id.Good to know
Scenes are data (objects, materials, lights, camera, keyframe tracks) drawn by DemoTake's own engine; nothing you send runs as code.
{
"name": "scene3d_create",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"preset": "floating-screen",
"timing": {
"scene": "1"
}
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay": {
"id": "ovl_m3ek3a0c4f8h2j6k",
"name": "Floating Screen",
"source": {
"kind": "scene3d",
"scene": {
"version": 1,
"background": {
"kind": "take",
"colors": []
},
"objects": [
{
"id": "screen",
"type": "recording",
"size": [
3.4,
1.9125,
0
]
}
]
}
},
"layer": "below-cursor"
},
"edit_id": "edt_m3ek3a0d1g5h9k2m"
}scene3d_add_object
Add 3D object
Add (or replace, same id) an object in a 3D scene: {id, type: box|roundedBox|sphere|cylinder|torus|plane|text|particles|model|image|recording, position, rotation (degrees), scale, size, radius, text, font, material {color, metalness, roughness, emissive, opacity}, particles {preset, count, seed}, asset}.
Parameters
take_id.overlay_id.{
"name": "scene3d_add_object",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek3a0c4f8h2j6k",
"object": {
"id": "badge",
"type": "text",
"text": "v2.0",
"position": [
1.9,
1.1,
0.3
],
"size": [
0,
0.35,
0.1
],
"material": {
"color": "#FFD166",
"metalness": 0.6,
"roughness": 0.25
}
}
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay": {
"id": "ovl_m3ek3a0c4f8h2j6k"
},
"edit_id": "edt_m3ek3b2n6p0r4s8t"
}scene3d_update
Update 3D scene
JSON merge patch on a 3D scene's state: {"camera":{"fov":30}}, {"background":{"kind":"color","colors":["#0b0f1a"]}}, {"post":{"vignette":0.4}}; per-object patches go in {"objects_by_id":{"logo":{"position":[0,1,0],"material":{"color":"#FF7AB6"}}}} (null removes that object). Validated and render-checked.
Parameters
take_id.overlay_id.Good to know
A null value removes a field (or, under objects_by_id, the object).
{
"name": "scene3d_update",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek3a0c4f8h2j6k",
"patch": {
"camera": {
"fov": 30
},
"objects_by_id": {
"badge": {
"material": {
"color": "#5EEAD4"
}
}
}
}
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay": {
"id": "ovl_m3ek3a0c4f8h2j6k"
},
"edit_id": "edt_m3ek3c9w1x5z7a3b"
}scene3d_animate
Animate 3D scene
Add keyframe tracks ({target:"logo.rotation", keyframes:[{t:0,value:[0,0,0]},{t:3,value:[0,360,0],ease:"easeInOut"}], loop}) — tracks for the same target are replaced — and/or a camera move preset (orbit, dolly, push-in) from start for duration seconds, e.g. "orbit the camera around the logo over 3 s".
Parameters
take_id.overlay_id.Good to know
Times are seconds from the overlay's start. Eases: linear, easeIn, easeOut, easeInOut, spring, step.
{
"name": "scene3d_animate",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek3a0c4f8h2j6k",
"camera": "orbit",
"start": 0.5,
"duration": 3,
"tracks": [
{
"target": "badge.rotation",
"keyframes": [
{
"t": 0,
"value": [
0,
-90,
0
]
},
{
"t": 1.2,
"value": [
0,
0,
0
],
"ease": "spring"
}
]
}
]
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay": {
"id": "ovl_m3ek3a0c4f8h2j6k"
},
"edit_id": "edt_m3ek3d4e8f2g6h0j"
}scene3d_remove
Remove 3D object or scene
Remove one object (and its tracks) from a 3D scene, or the whole 3D overlay when object_id is omitted.
Parameters
take_id.overlay_id.{
"name": "scene3d_remove",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek3a0c4f8h2j6k",
"object_id": "badge"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlays": [
{
"id": "ovl_m3ek3a0c4f8h2j6k",
"name": "Floating Screen",
"kind": "scene3d"
}
]
}scene3d_preview
Preview 3D scene
PNG of a 3D overlay's frame (composited over the video, or isolated:true alone), exactly as exported.
Parameters
take_id.overlay_id.{
"name": "scene3d_preview",
"arguments": {
"take_id": "take_m3ehbyc0k7qv2x9d",
"overlay_id": "ovl_m3ek3a0c4f8h2j6k",
"output_time": 2.4
}
}{
"path": "/var/folders/…/T/DemoTake-Previews/take_m3ehbyc0k7qv2x9d/overlays-2.40.png",
"width": 1280,
"height": 720
}permissions_request
Request permission
Ask macOS for a permission DemoTake needs (shows the system prompt or opens System Settings › Privacy & Security). The user must approve; check permissions_status afterwards.
Parameters
Good to know
state is granted, denied, not_determined or unavailable; check permissions_status after the user approves.
{
"name": "permissions_request",
"arguments": {
"permission": "accessibility"
}
}{
"permission": "accessibility",
"state": "not_determined"
}export_cancel
Cancel render
Cancel a queued or running render job (from export_render). Finished jobs are returned unchanged.
Parameters
job_id.{
"name": "export_cancel",
"arguments": {
"job_id": "exp_m3ej19z1q2dz7vhn"
}
}{
"job_id": "exp_m3ej19z1q2dz7vhn",
"take_id": "take_m3ehbyc0k7qv2x9d",
"format": "9:16",
"status": "cancelled",
"progress": 0.38
}demo_create
Create a demo (one call)
The one-call path: DemoTake plans scenes from the goal, rehearses, records, verifies and renders on its own (Director mode; needs DemoTake AI, hosted or with your own key, and DemoTake Agent to operate the app). Use the take_* / capture_* primitives instead if you want to drive the app yourself.
Parameters
target_id is accepted as an alias. Defaults to the take's target.12 fields
target.12 fields
Good to know
It returns as soon as the take is planned and the Director keeps working in the background; if DemoTake AI or DemoTake Agent isn't available you get a planned take (ai_mode byo_agent) to drive yourself.
{
"name": "demo_create",
"arguments": {
"goal": "Sign in, create an invoice for Northwind Studio and export it as PDF",
"target": "window:4127",
"style": "calm, no zoom on typing",
"outputs": [
"16:9"
],
"target_duration": 45,
"project_name": "Ledgerly"
}
}{
"take_id": "take_m3ehbyc0k7qv2x9d",
"project_id": "prj_m119ecc0t4w8nq2h",
"name": "Sign in, create an invoice for",
"goal": "Sign in, create an invoice for Northwind Studio and export it as PDF",
"purpose": "marketing",
"status": "draft",
"ai_mode": "director",
"execution_mode": "background_semantic",
"target": {
"target_id": "window:4127",
"kind": "window",
"name": "Ledgerly — Invoices",
"bundle_id": "com.example.Ledgerly",
"app_name": "Ledgerly",
"window_title": "Invoices",
"window_id": 4127,
"pid": 51234
},
"scenes": [
{
"scene_id": "scn_m3ehbzb8a1fr6mzp",
"index": 1,
"title": "Sign in",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"index": 2,
"title": "Create invoice",
"status": "pending",
"attempts": 0
},
{
"scene_id": "scn_m3ehbzbap5hn0cxv",
"index": 3,
"title": "Export PDF",
"status": "pending",
"attempts": 0
}
],
"assertions": [],
"assertions_passed": "0/0",
"event_count": 0,
"source_duration": 0,
"outputs": [
"16:9"
],
"edits": {
"compress_waits": {
"threshold": 1,
"to": 0.4
},
"target_duration": 45
},
"updated_at": "2026-09-26T09:40:00Z",
"next": "Optionally take_rehearse to dry-run, then capture_start."
}events_poll
Poll events
Realtime events after a cursor (scene_started, action_performed, classification, assertion_passed/failed, checkpoint_created, human_input_required, handoff_started / handoff_ended {outcome}, page changes of browser takes (info, data.url), render_progress…). Pass the returned cursor next time. Clients with notification support also receive these as notifications/message.
Parameters
take_id.Good to know
Clients that support MCP notifications also receive these as notifications/message, so polling is optional.
{
"name": "events_poll",
"arguments": {
"after": 40,
"take_id": "take_m3ehbyc0k7qv2x9d",
"limit": 50
}
}{
"events": [
{
"id": 41,
"kind": "scene_started",
"level": "info",
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"message": "Recording scene “Create invoice”",
"at": "2026-09-26T09:46:02Z",
"data": {
"segment_id": "seg_m3ehe1k0v7nq4cxz"
}
},
{
"id": 42,
"kind": "assertion_passed",
"level": "success",
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"message": "✓ Invoice created",
"at": "2026-09-26T09:46:25Z",
"data": {
"assertion_id": "ast_m3ehfkj0r9vb4ngs"
}
},
{
"id": 43,
"kind": "checkpoint_created",
"level": "success",
"take_id": "take_m3ehbyc0k7qv2x9d",
"scene_id": "scn_m3ehbzb9d3kw7tbe",
"message": "Checkpoint: Invoice INV-0042 open with 2 line items",
"at": "2026-09-26T09:46:27Z"
}
],
"cursor": 43,
"latest": 43
}demotake_guide
How to use DemoTake
Workflow guide for recording a demo with DemoTake. Read once if unsure which tools to call.
Parameters
None.
Good to know
Returns plain text only (no structuredContent), so the response above shows the MCP content block; the guide adapts to the edition that is running.
{
"name": "demotake_guide",
"arguments": {}
}{
"content": [
{
"type": "text",
"text": "DemoTake — your agent's camera crew (DemoTake Agent).\n\nWorkflow\n1. targets_list → pick the app window. targets_capabilities tells you the execution mode.\n2. take_create {name, goal, target, scenes:[{title,intent}], outputs:[\"16:9\",\"9:16\"]} → take_id.\n…"
}
]
}music_generate
Generate background music
Generate AI background music for a take and put it on the music lane (looped, loudness-normalised to -16 LUFS, ducked under narration). tracks > 1 makes a playlist of distinct tracks in one style, crossfaded and looped to fill long videos (use 6–10 for multi-hour compositions; one generation per track). Uses DemoTake AI on hosted plans (its own monthly song allowance, never the AI edit credits) or the user's own AI provider key (BYOK). dry_run:true returns the cost estimate and the allowance without generating; show it to the user before generating many tracks. Music is instrumental unless instrumental:false. Generated music carries an inaudible AI watermark; see the licensing notes in the docs before monetizing.
Parameters
take_id.{
"name": "music_generate",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"prompt": "rainy night, soft Rhodes",
"style": "lofi",
"tracks": 1
}
}{
"assets": [
{
"asset_id": "aud_m11a9k2h7q3v5x8z",
"title": "Chill lofi",
"source": "generated",
"file": "audio/aud_m11a9k2h7q3v5x8z.mp3",
"duration": 148.2,
"loudness_lufs": -11.4,
"prompt": "rainy night, soft Rhodes",
"style": "lofi",
"model": "music"
}
],
"estimate": {
"tracks": 1,
"length": "song",
"songs_of_allowance": 1
},
"allowance": "About 29 songs left this month",
"route": "hosted"
}music_add
Add or adjust music
Add the user's own audio file (path on this Mac; MP3/M4A/WAV/AIFF/FLAC) as background music (lane "music", looped), narration (lane "voice": ducks the music) or a sound effect (lane "sfx"); and/or adjust the music lane: volume (linear 0–1, or volume_db to change relatively, e.g. -3), ducking (duck:true/false, duck_db e.g. -15 = how far it drops under speech), fade_in/fade_out seconds, mute. "Lower the music under my voice" = duck:true, duck_db:-15.
Parameters
take_id.{
"name": "music_add",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"duck": true,
"duck_db": -15,
"volume_db": -3
}
}{
"take_id": "take_m11a2b3c4d5e6f7g",
"music": {
"lanes": [
{
"kind": "music",
"volume": 0.2,
"ducking": "-15 dB",
"loudness_target": -16
}
]
}
}music_remove
Remove music
Remove one music clip (clip_id), every use of one audio file (asset_id), or all music (neither). Files stay in the take; edit_revert restores.
Parameters
music.clips.music.assets.take_id.{
"name": "music_remove",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g"
}
}{
"take_id": "take_m11a2b3c4d5e6f7g"
}composition_create
Create a composition (study-with-me video)
Create a Composition: a long-form video made from media instead of a screen recording, e.g. a study-with-me / pomodoro video. Either pass request in plain words ("2.5-hour study-with-me video, 45/10 pomodoro, rainy cozy room, lofi music") or the fields. Builds the schedule (whole focus phases; 2.5 h at 45/10 = 3 cycles, 2:35:00), a timer, phase-change chimes (synthesized by DemoTake, royalty-free), YouTube chapters and the background (background_path: an image or looping video on this Mac; otherwise a calm gradient matching the scene). music:"generate" makes a playlist (music_tracks, default ~1 per 15 min, max 10) — call with music:"estimate" first to show the cost; music_path uses the user's own file. Export with export_render {take_id} (1080p or 4K HEVC MP4 + chapters.txt + thumbnail.jpg).
Parameters
scene.project.{
"name": "composition_create",
"arguments": {
"request": "make a 2.5-hour study-with-me video, 45/10 pomodoro, rainy cozy room, lofi music",
"background_path": "~/Pictures/rainy-room.jpg",
"music": "estimate"
}
}{
"take": {
"take_id": "take_m11b7c2d9e4f1g6h",
"name": "Study with me — 2.6 h (45/10)",
"composition": {
"duration": 9300,
"schedule": {
"focus_minutes": 45,
"break_minutes": 10,
"cycles": 3
}
}
},
"chapters": "0:00 Focus 1\n45:00 Break 1\n55:00 Focus 2\n1:40:00 Break 2\n1:50:00 Focus 3\n",
"music": {
"generated": false,
"tracks": 10,
"songs_of_allowance": 10,
"allowance": "About 30 songs left this month"
}
}voices_list
List voices
Voices for the voiceover. engine "device": the Mac's own voices for language (best first: Premium, then Enhanced; free and offline — the user can install better ones in System Settings › Accessibility › Spoken Content). engine "hosted": DemoTake's AI voices (each speaks the language of the text; available says whether this account can use them, with the remaining voice minutes). Without engine both are listed.
Parameters
{
"name": "voices_list",
"arguments": {
"language": "en-US"
}
}{
"engines": [
{
"engine": "device",
"available": true,
"default_voice": "com.apple.voice.premium.en-US.Ava",
"allowance": "Free, on this Mac",
"voices": [
{
"id": "com.apple.voice.premium.en-US.Ava",
"name": "Ava (Premium)",
"language": "en-US",
"quality": "premium",
"gender": "female"
},
{
"id": "com.apple.voice.enhanced.en-US.Evan",
"name": "Evan (Enhanced)",
"language": "en-US",
"quality": "enhanced",
"gender": "male"
}
]
},
{
"engine": "hosted",
"available": true,
"default_voice": "Charon",
"note": "Every voice speaks the language of the text.",
"allowance": "About 96 AI voice minutes left this month",
"voices": [
{
"id": "Charon",
"name": "Charon",
"quality": "hosted",
"tone": "Informative"
},
{
"id": "Sulafat",
"name": "Sulafat",
"quality": "hosted",
"tone": "Warm"
}
]
}
]
}voiceover_script
Write the voiceover script
Write (or rewrite) the voiceover: one short spoken line per scene, from the scene's intent, captions and steps. With DemoTake AI set up (or use_ai:true) the lines are written by DemoTake AI within each scene's word budget (its edited length at a natural ~150 words per minute); otherwise from the intents ("First, sign in…"). You can also write lines yourself with voiceover_update. Lines are anchored to their scene's start, so they follow trims and cuts. Returns the lines with their estimated timing and fit warnings; nothing is spoken until voiceover_generate.
Parameters
take_id.{
"name": "voiceover_script",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"style": "tutorial",
"language": "en-US"
}
}{
"language": "en-US",
"style": "tutorial",
"lines": [
{
"line_id": "nar_m11b1c2d3e4f5g6h",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"scene": "Sign in",
"text": "First, sign in with the demo account.",
"audio": "none",
"seconds": 2.4,
"start": 0,
"end": 2.4,
"estimated": true
},
{
"line_id": "nar_m11b7h8j9k0m1n2p",
"scene_id": "scn_m11a9h8g7f6e5d4c",
"scene": "Create a project",
"text": "Then create a project from the sidebar.",
"audio": "none",
"seconds": 2.4,
"start": 6.4,
"end": 8.8,
"estimated": true
}
]
}voiceover_generate
Speak the voiceover
Turn the voiceover lines into speech and put them on the voice lane (music ducks under them). Writes the script from the intents first if there is none. engine "device" (default) uses the Mac's own voices — free, offline; "hosted" uses DemoTake's AI voices (monthly voice minutes on Director/Team/Enterprise, or the user's own AI key). voice: an id or name from voices_list (default: the take's voice, else the best one for the language). Only lines without current audio are spoken unless regenerate:true or the voice changes. Audio is never sped up: lines that run past their scene are reported in warnings; fit:true holds the scene's last frame just long enough. dry_run:true reports the lines and the estimated seconds without speaking. When the take's captions follow the voiceover (voiceover_update {sync_captions:true}) they're rewritten per sentence. Localized takes: language set to one of the take's languages (take_languages_set) speaks that language's translated voiceover (translate_take first) with its own voice — the Mac's voice for that language (voices_list {language}) or the AI voices, which speak every language — without touching the take's own voiceover.
Parameters
take_id.{
"name": "voiceover_generate",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"engine": "device",
"voice": "Ava"
}
}{
"voiceover": {
"language": "en-US",
"voice": {
"engine": "device",
"id": "com.apple.voice.premium.en-US.Ava",
"name": "Ava (Premium)"
},
"style": "tutorial",
"lines": [
{
"line_id": "nar_m11b1c2d3e4f5g6h",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"scene": "Sign in",
"text": "First, sign in with the demo account.",
"audio": "current",
"seconds": 2.6,
"start": 0,
"end": 2.6
},
{
"line_id": "nar_m11b7h8j9k0m1n2p",
"scene_id": "scn_m11a9h8g7f6e5d4c",
"scene": "Create a project",
"text": "Then create a project from the sidebar.",
"audio": "current",
"seconds": 2.6,
"start": 6.4,
"end": 9
}
]
},
"spoken": [
"nar_m11b1c2d3e4f5g6h",
"nar_m11b7h8j9k0m1n2p"
],
"seconds": 5.2,
"engine": "device",
"allowance": "Free, on this Mac"
}voiceover_update
Edit the voiceover
Change one voiceover line (line_id, or the line of scene; empty text removes it; a new scene gets a new line), where it starts (at: scene_start | checkpoint | event with event_id, offset seconds), and/or voiceover options: mute_microphone (silence the recording's microphone in scenes with a voiceover), sync_captions (captions = the voiceover, one per sentence, timed with the speech; true rewrites them now), volume (linear, 1 = unity), style, language. fit:true holds scenes whose line runs past their end (never speeds audio up). Changed text needs voiceover_generate to be spoken again. With language set to one of the take's translation languages, text (with line_id) edits that language's line and fit:true holds scenes in that language only. edit_revert undoes it.
Parameters
scene_id is accepted as an alias.scene.take_id.{
"name": "voiceover_update",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"scene": "2",
"text": "Then create a project straight from the sidebar.",
"mute_microphone": true
}
}{
"language": "en-US",
"style": "tutorial",
"mute_microphone": true,
"lines": [
{
"line_id": "nar_m11b1c2d3e4f5g6h",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"scene": "Sign in",
"text": "First, sign in with the demo account.",
"audio": "current",
"seconds": 2.6,
"start": 0,
"end": 2.6
},
{
"line_id": "nar_m11b7h8j9k0m1n2p",
"scene_id": "scn_m11a9h8g7f6e5d4c",
"scene": "Create a project",
"text": "Then create a project straight from the sidebar.",
"audio": "stale",
"seconds": 2.4,
"start": 6.4,
"end": 8.8,
"estimated": true
}
]
}voiceover_remove
Remove the voiceover
Remove voiceover lines (line_id, or every line of scene), or the whole voiceover (neither). Audio files stay in the take; edit_revert restores.
Parameters
scene_id is accepted as an alias.scene.take_id.{
"name": "voiceover_remove",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"scene": "2"
}
}{
"language": "en-US",
"style": "tutorial",
"lines": [
{
"line_id": "nar_m11b1c2d3e4f5g6h",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"scene": "Sign in",
"text": "First, sign in with the demo account.",
"audio": "current",
"seconds": 2.6,
"start": 0,
"end": 2.6
}
]
}export_subtitles
Export subtitles and transcript
Write subtitle and transcript files for the take's current cut into its exports folder, timed in output time (after trims, cuts, speed changes and holds): srt, vtt (WebVTT), txt (plain transcript), md (Markdown transcript with scene headings). source: auto (the voiceover when there is one, else the captions — shown or not), captions or narration. Subtitle files are skipped when there's nothing to say. Also available on export_render {subtitles:[…], embed_subtitles:true} next to a video.
Parameters
take_id.{
"name": "export_subtitles",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"formats": [
"srt",
"vtt",
"md"
]
}
}{
"files": [
"~/Movies/DemoTake/Takes/Checkout.take/exports/checkout-20261027-101500.srt",
"~/Movies/DemoTake/Takes/Checkout.take/exports/checkout-20261027-101500.vtt",
"~/Movies/DemoTake/Takes/Checkout.take/exports/checkout-20261027-101500-transcript.md"
]
}take_languages_set
Set the take's languages
Localized takes: one take, many languages. Set the languages the take is translated into (languages: replaces the list; add / remove change it), the language the take is written in (source_language; default: the voiceover's, else en-US) and the glossary: terms kept as written in every language (product names, button labels the app keeps in English), or translated one fixed way ({term:"Projects", translations:{de:"Projekte"}}). Removing a language drops its translations and voiceover (edit_revert brings them back). Then translate_take translates, voiceover_generate {language} speaks a language, export_render {languages} exports a video per language. Returns the localization status per language.
Parameters
3 fields
take_id.{
"name": "take_languages_set",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"add": [
"de",
"sv"
],
"glossary": [
"Aurora",
{
"term": "Projects",
"translations": {
"de": "Projekte"
}
}
]
}
}{
"source_language": "en-US",
"languages": [
{
"language": "de",
"name": "German",
"translated": 0,
"changed": 0,
"missing": 9
},
{
"language": "sv",
"name": "Swedish",
"translated": 0,
"changed": 0,
"missing": 9
}
],
"glossary": [
{
"term": "Aurora"
},
{
"term": "Projects",
"translations": {
"de": "Projekte"
}
}
]
}translate_take
Translate the take
Translate the take into its languages (take_languages_set first): captions, the voiceover lines (each within its scene's word budget in that language), the guide's scene titles and descriptions, overlay text, selected on-screen text (onscreen_text_scan) and interactive demo tips (kinds:["interactive"]). engine "ai" (DemoTake AI on Director/Team/Enterprise, which counts toward the plan's DemoTake AI use; or the user's own AI provider key) or "device" (Apple's on-device translation, macOS 26+, languages installed in System Settings › General › Language & Region; free and offline). Glossary terms, names, numbers, keyboard shortcuts, placeholders ({name}, %@), URLs, emails and anything Secret Guard flags are kept on the Mac and restored exactly; never any pixels. Only missing and changed translations are translated; force:true redoes machine translations too. Translations written by hand (translation_update) are kept unless their source text changed. Returns per language what was translated, translations to check (a protected name/number was lost) and warnings (voiceover lines too long for their scene: shorten, or voiceover_update {fit:true, language}).
Parameters
take_id.{
"name": "translate_take",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"languages": [
"de"
],
"kinds": [
"captions",
"narration",
"guide"
]
}
}{
"engine": "ai",
"characters": 412,
"results": [
{
"language": "de",
"translated": [
"take/name",
"scene/scn_m11a3b4c5d6e7f8g/title",
"caption/edt_m11c2d3e4f5g6h7j",
"narration/nar_m11b1c2d3e4f5g6h"
],
"kept_edits": 1,
"warnings": [
"German: the voiceover of “Create a project” runs 0.8 s past the end of the scene. Shorten it, or hold the scene (voiceover_update {fit: true, language})."
]
}
]
}translation_update
Edit a translation
Write or correct translations by hand (kept when the take is translated again, unless the source text changes). Give key + text for one, or updates: [{key, text}] for several; keys come from take_get (localization.languages[].texts[].key, e.g. "caption/edt_…", "narration/nar_…", "scene/scn_…/title", "onscreen/ost_…"). Empty text removes the translation. A changed voiceover line needs voiceover_generate {language} to be spoken again. edit_revert undoes it.
Parameters
2 fields
take_id.{
"name": "translation_update",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"language": "de",
"key": "narration/nar_m11b1c2d3e4f5g6h",
"text": "Melde dich zuerst mit dem Demo-Konto an."
}
}{
"source_language": "en-US",
"languages": [
{
"language": "de",
"name": "German",
"translated": 9,
"changed": 0,
"missing": 0,
"edited": 1,
"texts": [
{
"key": "narration/nar_m11b1c2d3e4f5g6h",
"kind": "narration",
"scene": "Sign in",
"source": "First, sign in with the demo account.",
"text": "Melde dich zuerst mit dem Demo-Konto an.",
"state": "current",
"edited": true
}
]
}
]
}onscreen_text_scan
Find text on screen
Read the text visible in each scene of the cut (on-screen text recognition on this Mac, a few frames per scene) so it can be shown translated: each selected string becomes a small callout beside the text it translates in every language's video (it follows the camera and never covers the text). Text inside or touching a redaction is never read (redactions are painted over first), anything Secret Guard flags is skipped, and no pixels leave the Mac — only the selected strings are translated (translate_take {kinds: ["onscreen"]}). Large text and text near a click or the focused element are selected by default; select / deselect change that without scanning again. Scanning again keeps ids, selections and translations of text found again.
Parameters
take_id.{
"name": "onscreen_text_scan",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g"
}
}{
"found": 3,
"selected": 2,
"texts": [
{
"id": "ost_m11d1e2f3g4h5j6k",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"scene": "Sign in",
"text": "Welcome back",
"selected": true,
"prominence": 1
},
{
"id": "ost_m11d7k8m9n0p1q2r",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"scene": "Sign in",
"text": "Continue with email",
"selected": true,
"near_focus": true,
"prominence": 0.42
},
{
"id": "ost_m11d3s4t5v6w7x8y",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"scene": "Sign in",
"text": "Terms of use",
"selected": false,
"prominence": 0.3
}
]
}interactive_build
Build the interactive demo
Interactive (click-through) demo of a recorded take: a still per step, a pulsing hotspot where the click happened and a short tooltip; viewers click the hotspot (or Next). Steps come from the cut in output order: each scene's start (a chapter title), every click, typing (the still shows the typed text) and the scene's checkpoint; cut-out ranges, removed events and excluded scenes have none. Hotspots come from the clicked element (or a small square around the click) mapped through crop and camera; tooltips from the caption on screen, else the voiceover sentence, else a sentence about the click, else the scene's intent. Stills are rendered from the edited, redacted output (captions off: the tooltip says it). reset:false (default) keeps your edits, order and deletions and adds steps for new clicks; reset:true starts over. Then interactive_update edits steps, interactive_export writes the offline HTML demo, interactive_share makes a link with an embed code. edit_revert undoes it.
Parameters
take_id.{
"name": "interactive_build",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g"
}
}{
"built": true,
"language": "en-US",
"title": "Create a project",
"canvas": {
"width": 1920,
"height": 1080
},
"options": {
"voiceover": true,
"chapters": true
},
"steps": [
{
"step_id": "ist_scn_m11a3b4c5d6e7f8g",
"number": 1,
"kind": "chapter",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"output_time": 0,
"text": "Create a project from the dashboard",
"text_from": "chapter",
"advance": "click"
},
{
"step_id": "ist_evt_m11e1f2g3h4j5k6m",
"number": 2,
"kind": "click",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"event_id": "evt_m11e1f2g3h4j5k6m",
"output_time": 1.18,
"hotspot": {
"x": 0.8125,
"y": 0.0954,
"width": 0.0315,
"height": 0.056
},
"text": "Click New Project to start",
"text_from": "caption",
"advance": "click"
},
{
"step_id": "ist_evt_m11e7f8g9h0j1k2m",
"number": 3,
"kind": "type",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"event_id": "evt_m11e7f8g9h0j1k2m",
"output_time": 3.42,
"hotspot": {
"x": 0.36,
"y": 0.38,
"width": 0.18,
"height": 0.04
},
"text": "Type “Launch plan” in Project name.",
"text_from": "event",
"advance": "click"
},
{
"step_id": "ist_cp_scn_m11a3b4c5d6e7f8g",
"number": 4,
"kind": "checkpoint",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"output_time": 5.95,
"text": "The project opens with its first board.",
"text_from": "event",
"advance": "click"
}
]
}interactive_get
Get the interactive demo
The interactive demo's steps as the export shows them: step_id, kind (chapter, click, type, checkpoint), the still's output_time, the hotspot normalized to the still (null = a Next button), the tooltip text and where it comes from (text_from: caption, narration, event, intent, chapter or custom), advance (click or auto), edited fields, without_zoom (the camera hid the hotspot, so that still is drawn without camera moves), plus steps whose frame was cut (skipped) and warnings (a hotspot off-screen even without zoom). language: one of the take's languages (texts translated; translate_take {kinds:["interactive"]} translates custom and event tooltips). Works before interactive_build (derived on the fly; built:false). preview_frame {output_time} shows a step's still (with captions; the export hides them).
Parameters
take_id.{
"name": "interactive_get",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"language": "de"
}
}{
"built": true,
"language": "de",
"title": "Create a project",
"canvas": {
"width": 1920,
"height": 1080
},
"options": {
"voiceover": true,
"chapters": true
},
"steps": [
{
"step_id": "ist_scn_m11a3b4c5d6e7f8g",
"number": 1,
"kind": "chapter",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"output_time": 0,
"text": "Create a project from the dashboard",
"text_from": "chapter",
"advance": "click"
},
{
"step_id": "ist_evt_m11e1f2g3h4j5k6m",
"number": 2,
"kind": "click",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"event_id": "evt_m11e1f2g3h4j5k6m",
"output_time": 1.18,
"hotspot": {
"x": 0.8125,
"y": 0.0954,
"width": 0.0315,
"height": 0.056
},
"text": "Click New Project to start",
"text_from": "caption",
"advance": "click"
},
{
"step_id": "ist_evt_m11e7f8g9h0j1k2m",
"number": 3,
"kind": "type",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"event_id": "evt_m11e7f8g9h0j1k2m",
"output_time": 3.42,
"hotspot": {
"x": 0.36,
"y": 0.38,
"width": 0.18,
"height": 0.04
},
"text": "Type “Launch plan” in Project name.",
"text_from": "event",
"advance": "click"
},
{
"step_id": "ist_cp_scn_m11a3b4c5d6e7f8g",
"number": 4,
"kind": "checkpoint",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"output_time": 5.95,
"text": "The project opens with its first board.",
"text_from": "event",
"advance": "click"
}
]
}interactive_update
Edit the interactive demo
Edit interactive demo steps (step ids from interactive_get). One step: text (the tooltip; reset_text goes back to the looked-up text), hotspot {x,y,width,height} normalized to the step's still (remove_hotspot: a Next button instead; reset_hotspot: the click's own), output_time (show another frame of the same scene; reset_frame), placement (auto, top, bottom, left, right), advance ("auto": not clickable, moves on after duration seconds or its voiceover; "click"). Several: delete [ids], merge [ids] (into the first: its frame and hotspot, tooltips joined), order [ids] (these first, in this order), restore (bring deleted and merged steps back). options {voiceover, chapters}. Edits survive interactive_build refreshes; edit_revert undoes them. Returns the steps.
Parameters
4 fields
2 fields
take_id.{
"name": "interactive_update",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"step_id": "ist_evt_m11e7f8g9h0j1k2m",
"text": "Name the project"
}
}{
"built": true,
"language": "en-US",
"title": "Create a project",
"canvas": {
"width": 1920,
"height": 1080
},
"options": {
"voiceover": true,
"chapters": true
},
"steps": [
{
"step_id": "ist_scn_m11a3b4c5d6e7f8g",
"number": 1,
"kind": "chapter",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"output_time": 0,
"text": "Create a project from the dashboard",
"text_from": "chapter",
"advance": "click"
},
{
"step_id": "ist_evt_m11e1f2g3h4j5k6m",
"number": 2,
"kind": "click",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"event_id": "evt_m11e1f2g3h4j5k6m",
"output_time": 1.18,
"hotspot": {
"x": 0.8125,
"y": 0.0954,
"width": 0.0315,
"height": 0.056
},
"text": "Click New Project to start",
"text_from": "caption",
"advance": "click"
},
{
"step_id": "ist_evt_m11e7f8g9h0j1k2m",
"number": 3,
"kind": "type",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"event_id": "evt_m11e7f8g9h0j1k2m",
"output_time": 3.42,
"hotspot": {
"x": 0.36,
"y": 0.38,
"width": 0.18,
"height": 0.04
},
"text": "Name the project",
"text_from": "custom",
"advance": "click",
"edited": [
"text"
]
},
{
"step_id": "ist_cp_scn_m11a3b4c5d6e7f8g",
"number": 4,
"kind": "checkpoint",
"scene": "New project",
"scene_id": "scn_m11a3b4c5d6e7f8g",
"output_time": 5.95,
"text": "The project opens with its first board.",
"text_from": "event",
"advance": "click"
}
]
}interactive_export
Export the interactive demo
Write the interactive demo as an offline HTML demo: a folder (index.html opens from file:// with no server; the player, JPEG stills at 1920 and 960 px wide, voiceover clips, manifest.json) and a ZIP next to it, in the take's exports. Stills are rendered from the edited, redacted output. languages:["source","de"] (or "all") puts every language in one demo with a language switch. The same as export_render {formats:["interactive"]}. The export is validated (every image present, manifest, hotspots inside the stills) before it completes; export_validate {job_id} checks it again.
Parameters
take_id.{
"name": "interactive_export",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"languages": [
"source",
"de"
]
}
}{
"job_id": "exp_m11f1g2h3j4k5m6n",
"take_id": "take_m11a2b3c4d5e6f7g",
"format": "interactive",
"status": "completed",
"progress": 1,
"output_path": "/Users/you/Movies/DemoTake/Exports/create-a-project-interactive-20261027-101500",
"side_files": [
"/Users/you/Movies/DemoTake/Exports/create-a-project-interactive-20261027-101500.zip"
],
"languages": [
"en-US",
"de"
],
"file_size_bytes": 1843200,
"file_size_mb": 1.8
}schedule_create
Schedule re-recording
Re-record a take on a schedule or on every deploy (DemoTake Cloud, Team plan). The take must be in DemoTake Cloud (sync it first). A run replays the take's steps (or spec), re-records only scenes whose UI changed (only_changed), keeps your edits, exports the formats you ask for, compares each scene with the last run, and can update the take's share links in place (publish_mode auto, or approval). Runs execute on a runner: a Mac with DemoTake Agent (Settings › Account › Use this Mac as a runner) or demotake runner in CI on a Mac. executor "cloud" checks web specs in DemoTake's cloud browser without a Mac (checks only). With webhook true the result has trigger_token and hook_url, shown once: call POST <webhook.url> with Authorization: Bearer <token> after a deploy, or paste hook_url into Vercel/Netlify.
Parameters
take_id.{
"name": "schedule_create",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g",
"cron": "0 7 * * 1-5",
"timezone": "Europe/Stockholm",
"webhook": true,
"only_changed": true,
"formats": [
"16:9",
"9:16"
],
"publish_mode": "approval",
"notify_on": "changes",
"slack_url": "https://hooks.slack.com/services/T000/B000/XXXXXXXX"
}
}{
"schedule_id": "sch_m12a1b2c3d4e5f6g",
"take_id": "take_m11a2b3c4d5e6f7g",
"name": "Tidewell: funnel report re-recording",
"enabled": true,
"cron": "0 7 * * 1-5",
"timezone": "Europe/Stockholm",
"next_run_at": "2026-10-28T06:00:00.000Z",
"webhook": {
"enabled": true,
"url": "https://api.demotake.my/v1/schedules/sch_m12a1b2c3d4e5f6g/trigger",
"token_hint": "…h2Kp"
},
"options": {
"only_changed": true,
"record": true,
"target_url": null,
"languages": [],
"formats": [
"16:9",
"9:16"
],
"interactive": false
},
"executor": "runner",
"publish": {
"mode": "approval",
"share_ids": null
},
"notify": {
"on": "changes",
"email": "admins",
"slack": {
"hint": "hooks.slack.com/…XXXX"
},
"webhook": null
},
"trigger_token": "dtt_…(shown once)",
"hook_url": "https://api.demotake.my/v1/hooks/schedules/dtt_…"
}schedule_list
List schedules
Schedules of the signed-in workspace (one take's with take_id): cadence, next run, deploy webhook, options, publish mode, last status; plus this month's usage (runs, run minutes), the plan's limits and how many runners are online.
Parameters
take_id.{
"name": "schedule_list",
"arguments": {
"take_id": "take_m11a2b3c4d5e6f7g"
}
}{
"available": true,
"schedules": [
{
"schedule_id": "sch_m12a1b2c3d4e5f6g",
"name": "Tidewell: funnel report re-recording",
"cron": "0 7 * * 1-5",
"timezone": "Europe/Stockholm",
"last_status": "succeeded"
}
],
"usage": {
"period": "2026-10",
"runs": 14,
"run_minutes": 38.5
},
"limits": {
"runs_month": 600,
"min_interval_minutes": 60,
"max_concurrent_runs": 3
},
"runners_online": 1
}schedule_update
Change a schedule
Change a schedule's cadence, webhook, options, publishing or notifications, or pause it (enabled false). Only the fields you pass change. Turning webhook on returns a new trigger token once.
Parameters
{
"name": "schedule_update",
"arguments": {
"schedule_id": "sch_m12a1b2c3d4e5f6g",
"enabled": false,
"publish_mode": "auto"
}
}{
"schedule_id": "sch_m12a1b2c3d4e5f6g",
"enabled": false,
"next_run_at": null,
"publish": {
"mode": "auto",
"share_ids": null
}
}schedule_delete
Delete a schedule
Delete a schedule and its run history. Share links keep the version they show.
Parameters
{
"name": "schedule_delete",
"arguments": {
"schedule_id": "sch_m12a1b2c3d4e5f6g"
}
}{
"deleted": true,
"schedule_id": "sch_m12a1b2c3d4e5f6g"
}schedule_run
Run a schedule now
Queue a run of a schedule now (Run now). If one is already queued, that run is returned (coalesced). A runner picks it up; follow it with schedule_runs.
Parameters
{
"name": "schedule_run",
"arguments": {
"schedule_id": "sch_m12a1b2c3d4e5f6g"
}
}{
"run": {
"run_id": "run_m13a1b2c3d4e5f6g",
"status": "queued",
"trigger": "manual"
},
"coalesced": false
}schedule_runs
Run history
A schedule's recent runs (newest first), or one run with run_id: status (queued, running, succeeded, failed, cancelled, expired), publish_status (pending_approval, published, rolled_back, …), per-scene changed / unchanged / failed with reasons and before/after stills of the rendered (redacted) export, checks, exports, duration, the runner, and the review URL in DemoTake Web.
Parameters
{
"name": "schedule_runs",
"arguments": {
"run_id": "run_m13a1b2c3d4e5f6g"
}
}{
"run_id": "run_m13a1b2c3d4e5f6g",
"status": "succeeded",
"publish_status": "pending_approval",
"trigger": "webhook",
"trigger_meta": {
"source": "github",
"ref": "main",
"sha": "8c1f2e9"
},
"runner": {
"id": "rnr_m10a1b2c3d4e5f6g",
"name": "Studio Mac mini"
},
"run_seconds": 168,
"summary": {
"passed": true,
"changed": 1,
"regenerated": 1,
"assertions": {
"passed": 3,
"total": 3,
"failed": []
},
"scenes": [
{
"title": "Sign in",
"status": "unchanged",
"score": 0,
"reasons": []
},
{
"title": "Create a report",
"status": "changed",
"score": 0.18,
"reasons": [
"New: Cohort",
"Gone: Funnel (beta)"
],
"still_url": "https://api.demotake.my/v1/media/…",
"before_url": "https://api.demotake.my/v1/media/…"
}
]
},
"review_url": "https://demotake.my/app/take/take_m11a2b3c4d5e6f7g?schedules=sch_m12a1b2c3d4e5f6g&run=run_m13a1b2c3d4e5f6g"
}run_approve
Approve a run
Publish a run that waits for approval: the take's share links switch to its exports (same URLs, analytics kept). reject true keeps the current version instead.
Parameters
{
"name": "run_approve",
"arguments": {
"run_id": "run_m13a1b2c3d4e5f6g"
}
}{
"run_id": "run_m13a1b2c3d4e5f6g",
"status": "succeeded",
"publish_status": "published",
"published": [
{
"share_id": "shr_m11g1h2j3k4m5n6p",
"from_version": 1,
"to_version": 2
}
]
}run_rollback
Roll back a run
Undo a published run: its share links show the version they showed before (as a new version; URLs and analytics stay).
Parameters
{
"name": "run_rollback",
"arguments": {
"run_id": "run_m13a1b2c3d4e5f6g"
}
}{
"run_id": "run_m13a1b2c3d4e5f6g",
"publish_status": "rolled_back",
"restored": [
{
"share_id": "shr_m11g1h2j3k4m5n6p",
"from_version": 2,
"to_version": 3
}
]
}runner_run
Run one scheduled run here
Runner mode (DemoTake Agent): claim the oldest queued run of the runner's workspace, execute it on this Mac (re-record, export, compare, upload) and report it. Pass the runner token (dtr_…, from DemoTake Web › Schedules › Runners) or leave it out to use this Mac's own runner registration. Returns the finished run, or idle true when nothing was queued. demotake runner calls this in a loop.
Parameters
{
"name": "runner_run",
"arguments": {
"token": "dtr_…"
}
}{
"run_id": "run_m13a1b2c3d4e5f6g",
"status": "succeeded",
"publish_status": "pending_approval",
"summary": {
"changed": 1
}
}