---
name: runchat-canvas-api
description: Interact with a Runchat workflow canvas via REST API. Read, create, connect, update, and run nodes programmatically. Use when given a Runchat editor URL (runchat.com/editor?id=...) or asked to build/modify a workflow on a Runchat canvas.
---

# Runchat Canvas API

You have access to a Runchat canvas — a visual node-based workflow editor.
Use the tools below to read, create, connect, and run nodes on the canvas.

## Base URL

All tool calls are made via a single endpoint:

```
POST https://runchat.com/api/v1/{runchat_id}/canvas
Authorization: Bearer {api_key}
Content-Type: application/json

{
  "tool": "<tool_name>",
  "args": { ... }
}
```

The response is always:
```json
{ "result": { ... } }
```

## Important Notes

- **Ask before running**: The `run_nodes` tool triggers execution which may consume credits. Always confirm with the user before calling it.
- **Node types**: promptNode (LLM), inputNode (user input), codeNode (JavaScript), imageNode (media generation), noteNode (documentation), runChatNode (sub-workflow).
- **Workflow**: Typically: `get_canvas` → `read_nodes` → create/update/connect → `organize_nodes` → (optionally) `run_nodes`.
- **Connections**: Use `connect_nodes` to wire outputs of one node to inputs of another. Use `read_nodes` to discover handle names.
- **Published tools**: Use `search_tools` to find published tools/runchats by keyword (returns each tool's runchat_id and input/output parameter names). Then `execute_tool` to run one directly and get its outputs, `inspect_tool` to read how it's built, or `place_tool` (tool_id) to place it on this canvas. For `inspect_tool`/`execute_tool` the `runchat_id` in `args` names the TOOL to act on — the `{runchat_id}` in the URL is only the canvas/auth scope (it is ignored by these tools).

**Common handles:**
- promptNode — inputs: `messages` (any type), `prompt` (string) | outputs: `messages`
- codeNode — inputs: `code` (string) | outputs: `result`
- inputNode — outputs: `content`
- imageNode — inputs: from `get_model_params` | outputs: `image`

---

## Node Creation Tools

### `create_prompt_node`

Create an LLM node (promptNode) for reasoning, writing, analysis, or agent tasks. Wire data in with connect_nodes: 'prompt' takes the task text, 'messages' takes context/prior outputs/images from other nodes (accepts any type). Output is on the 'messages' handle; structured output (output_format) is on 'artifacts'.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `prompt` | string | yes | The task or question the model should perform. One task only — feeding N values into this input later runs the node N times. Persona/system text goes in `instruction`, not here. Use real line breaks, never the literal characters \n. |
| `model_id` | string | no | Optional api_id of the LLM to run this node (discover ids with list_models, type 'llm'). Omit to use the default model. |
| `instruction` | string | no | Optional system prompt / persona (a hidden input — cannot be connected to). |
| `output_format` | string | no | Optional output contract, routed to the 'artifacts' handle. "list" = array of strings (use when downstream nodes consume multiple distinct items). "html" = a complete HTML document (ALWAYS set this when the node's job is generating a website/page — the document lands on 'artifacts' typed as HTML). "code" = a single code block. Or pass a JSON schema string for custom structured output. Omit for plain text. |
| `tools` | string[] | no | Optional runchat_ids of published tools (from search_tools) the model can call while it runs. |
| `label` | string | no | Optional display label for the node. |
| `input_depths` | object | no | Optional per-input matching mode, keyed by input name: "each" (one run per item), "all" (pass the whole list as ONE grouped value in a single run), "flatten" (unwrap one nesting level). Numeric equivalents 0/1/-1 are also accepted. The default is per-input, NOT always "each": promptNode's "messages" and any array-typed model param default to "all", so a list arriving there produces ONE run over the whole list. To caption/describe items individually, set that input to "each" explicitly. Current values are visible as `depth` in read_nodes. See DATA MATCHING. |
| `position` | object | no | Optional explicit placement. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — the server positions the node from the anchor's real size, so no pixel math or spacing guesses. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit entirely to auto-place — nodes chain left-to-right off the existing workflow and organize_nodes tidies them afterwards. |

### `create_input_node`

Create a user-input widget node (inputNode). Its value is exposed on the 'content' output handle. Pick the widget with `format`; set a single starting value with `value`, a LIST of values with `values`, or `options`/`selected` for a select dropdown. One input node holds a whole list — to feed N values into a workflow create ONE node with `values`, never N separate nodes (see DATA MATCHING: downstream nodes run once per item).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `format` | `string` | `slider` | `toggle` | `image` | `file` | `select` | `button` | `code` | no | Widget type: 'string' text (default), 'slider' number, 'toggle' boolean, 'image' image upload/display, 'file' file upload, 'select' dropdown, 'button' trigger, 'code' code editor. |
| `value` | string | no | Initial single value. For format 'image', an image URL. Ignored for 'select' — use options/selected instead. |
| `values` | string[] | no | Initial LIST of values held by this one node (e.g. 3 prompts, 5 image URLs). Downstream nodes run once per item by default. Use instead of `value` for anything list-shaped; do NOT create one node per item. |
| `options` | string[] | no | For format 'select' only: the dropdown options. |
| `selected` | string | no | For format 'select' only: the initially selected option. Defaults to the first option. |
| `label` | string | no | Optional display label for the node. |
| `position` | object | no | Optional explicit placement. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — the server positions the node from the anchor's real size, so no pixel math or spacing guesses. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit entirely to auto-place — nodes chain left-to-right off the existing workflow and organize_nodes tidies them afterwards. |

### `create_image_node`

Create a media generation node (imageNode) — images, video, 3D, or audio. Requires a model_id from the AVAILABLE MODELS catalog. ALWAYS call get_model_params for the model first and use the returned parameter names as `params` keys. The generated media is on the 'image' output handle. For image generation or editing, if this conversation hasn't loaded it yet, call use_skill "image-prompting" BEFORE your first image node — its linework+reference recipe and input-image ordering are required for quality output.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `model_id` | string | yes | The api_id of the generation model (from the AVAILABLE MODELS catalog). |
| `params` | object | no | Model parameter values keyed by the parameter names from get_model_params, e.g. { "prompt": "a red chair", "aspect_ratio": "16:9" }. Values may be strings, numbers, or arrays. |
| `label` | string | no | Optional display label for the node. |
| `input_depths` | object | no | Optional per-input matching mode, keyed by input name: "each" (one run per item), "all" (pass the whole list as ONE grouped value in a single run), "flatten" (unwrap one nesting level). Numeric equivalents 0/1/-1 are also accepted. The default is per-input, NOT always "each": promptNode's "messages" and any array-typed model param default to "all", so a list arriving there produces ONE run over the whole list. To caption/describe items individually, set that input to "each" explicitly. Current values are visible as `depth` in read_nodes. See DATA MATCHING. |
| `position` | object | no | Optional explicit placement. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — the server positions the node from the anchor's real size, so no pixel math or spacing guesses. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit entirely to auto-place — nodes chain left-to-right off the existing workflow and organize_nodes tidies them afterwards. |

### `create_code_node`

Create a code node (codeNode). REQUIRED FIRST: if you have not already loaded the matching skill in this conversation, call use_skill for the language ('javascript' → 'javascript-code', 'html' → 'html-code', CAD languages → their skill) BEFORE this call — the skill defines the sandbox contract and code written without it regularly fails. Code executes on the SERVER only when the node is run (never on creation or edit). Output is on the fixed 'result' handle by default. Declare named input handles with `inputs` — each becomes connectable and readable in the code as a variable of the same name (inputs cannot reference undeclared code variables). If the code returns an object whose values should feed different downstream nodes, declare the keys in `outputs` so those handles exist and are connectable immediately.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `language` | `javascript` | `html` | `rhino-python` | `blender-python` | `revit-csharp` | `archicad-python` | no | 'javascript' (default) — sandboxed JavaScript/TypeScript, best for API calls and data transforms (use ENV.key for credentials). Supports multiple files and npm packages: add a package.json and extra modules with create_files after creation; top-level imports and `export default { fetch }` entries are handled automatically. 'html' — HTML/CSS/JS rendered in a live preview iframe; user-facing UIs and app frontends are authored here (the page can call window.runchat to run published tools). Also supports multiple files and npm packages: add browser ES modules (e.g. src/main.js), stylesheets, and a package.json with create_files, referenced from index.html via <script type="module" src="/src/main.js"> — everything is bundled and inlined into one self-contained page at render/run time. Declare `inputs` to make it parametric — they inject as same-named globals and the preview re-renders on change; don't build the HTML string in a separate node. CAD languages run in the connected CAD application via the bridge. |
| `code` | string | no | The source code. |
| `inputs` | object | no | Optional named input handles with default values, e.g. { "shape": "cube" }. Each is injected as a global variable of that exact name — use `shape` in the code, NOT `inputs.shape`. Applies to html nodes too: connected inputs become globals and the preview re-renders when they change. |
| `outputs` | string[] | no | Optional named output handles, e.g. ["summary", "imageUrl"]. Declare these ONLY when the code returns an object with exactly these keys — each key becomes a connectable output handle immediately (no need to run the node first). When the run executes, each handle is populated from the matching key of the returned object. Omit when the code returns a plain value (it lands on "result"). |
| `label` | string | no | Optional display label for the node. |
| `input_depths` | object | no | Optional per-input matching mode, keyed by input name: "each" (one run per item), "all" (pass the whole list as ONE grouped value in a single run), "flatten" (unwrap one nesting level). Numeric equivalents 0/1/-1 are also accepted. The default is per-input, NOT always "each": promptNode's "messages" and any array-typed model param default to "all", so a list arriving there produces ONE run over the whole list. To caption/describe items individually, set that input to "each" explicitly. Current values are visible as `depth` in read_nodes. See DATA MATCHING. |
| `position` | object | no | Optional explicit placement. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — the server positions the node from the anchor's real size, so no pixel math or spacing guesses. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit entirely to auto-place — nodes chain left-to-right off the existing workflow and organize_nodes tidies them afterwards. |

### `create_note`

Create a markdown note node (noteNode) for documentation, summaries, research findings, links, or images (markdown syntax). Do not create empty notes.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `content` | string | yes | The markdown content of the note. |
| `label` | string | no | Optional display label for the node. |
| `position` | object | no | Optional explicit placement. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — the server positions the node from the anchor's real size, so no pixel math or spacing guesses. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit entirely to auto-place — nodes chain left-to-right off the existing workflow and organize_nodes tidies them afterwards. |

### `place_tool`

Place a published tool (found with search_tools) onto the canvas as a runChatNode, labelled with the tool's own published name. Default: one collapsed, reusable tool node with auto-configured inputs — best when the user wants to use the tool as-is. Pass expand:true to instead copy the tool's full internal workflow onto the canvas as independent editable nodes (with its connections) so you can ADAPT it — modify the copies and wire them in with connect_nodes. When you only need a tool's result (not a node the user keeps), use execute_tool instead.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tool_id` | string | yes | The runchat_id of the published tool (from search_tools). |
| `expand` | boolean | no | When true, copy the tool's full internal node graph onto the canvas as editable nodes instead of one collapsed tool node. |
| `inputs` | object | no | Optional input values keyed by the tool's input parameter names (search_tools returns them). |
| `input_depths` | object | no | Optional per-input matching mode, keyed by input name: "each" (one run per item), "all" (pass the whole list as ONE grouped value in a single run), "flatten" (unwrap one nesting level). Numeric equivalents 0/1/-1 are also accepted. The default is per-input, NOT always "each": promptNode's "messages" and any array-typed model param default to "all", so a list arriving there produces ONE run over the whole list. To caption/describe items individually, set that input to "each" explicitly. Current values are visible as `depth` in read_nodes. See DATA MATCHING. |
| `position` | object | no | Optional explicit placement. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — the server positions the node from the anchor's real size, so no pixel math or spacing guesses. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit entirely to auto-place — nodes chain left-to-right off the existing workflow and organize_nodes tidies them afterwards. |

### `create_artifact_node`

Create an artifact node (artifactNode) that saves or fetches an artifact AS A WORKFLOW STEP. Only use this node when the artifact must be created or refreshed BY the workflow each time it runs — e.g. a scheduled workflow that regenerates a page, or a pipeline whose output is the artifact content. When YOU are authoring/publishing an artifact yourself (a blog post, website, or app UI the user asked you to build), call create_artifact / update_artifact directly instead — no node needed. Operation 'create' saves/updates content; 'get' fetches an existing artifact by id. The artifact type is inferred from the content when published: markdown → blog, HTML → website, and HTML that talks to the window.runchat app bridge → app. Do NOT author app HTML directly here — build and test it in an html code node first (live preview; window.runchat.run/state/user work on the canvas), then connect that node's result into this node's content. Call use_skill "app-artifact" before building one.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | `create` | `get` | no | 'create' to save/update artifact content (default), 'get' to fetch an existing artifact by ID. |
| `name` | string | no | Artifact name (create mode). |
| `content` | string | no | Content to save — markdown (→ blog), HTML (→ website), or app HTML that uses window.runchat (→ app). The artifact type is inferred from the content format at publish time. |
| `artifact_id` | string | no | Only when fetching or updating an existing artifact. In create mode, supplying an id makes every run UPDATE that artifact in place (stable URL) instead of creating a new one. |
| `folder` | string | no | Optional folder path, e.g. "myFolder/subFolder". Sets the folder in create mode or moves an existing artifact. |
| `label` | string | no | Optional display label for the node. |
| `position` | object | no | Optional explicit placement. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — the server positions the node from the anchor's real size, so no pixel math or spacing guesses. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit entirely to auto-place — nodes chain left-to-right off the existing workflow and organize_nodes tidies them afterwards. |

---

## Canvas Tools

### `get_canvas`

Get a high-level overview of all nodes and edges on the user's current canvas. Returns node IDs, types, labels, positions, and edge connections, plus the canvas's runchat_id and its latest published version (null if never published) — the runchat_id doubles as the published tool id for execute_tool / runchat.run / app UIs. For large canvases the response is paginated (50 nodes per page). When has_more is true, call again with the returned next_offset to fetch the next page. Do not call this to verify nodes you just created.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `offset` | number | no | Pagination offset. Omit for the first page. Use the next_offset value from a previous response to fetch the next page. |

### `read_nodes`

Read the full state of one or more nodes by ID. Returns each parameter's name, type, label, status, and current data values. Parameter data is truncated by default; when truncated the response includes the original data length so you can decide whether to re-read with a larger data_limit or with data_offset to page through. Use this to inspect what a node accepts, what data it has, and to find handle names for connect_nodes. Call get_canvas first to find node IDs.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_ids` | string[] | yes | Array of node IDs to read (from get_canvas) |
| `data_limit` | number | no | Maximum number of characters of each parameter's data to return. Defaults to 4000. Pass 0 to return the full data with no truncation. Increase only when you need more of a specific value. |
| `data_offset` | number | no | Character offset into each parameter's data before applying data_limit. Defaults to 0. Use this together with data_limit to page through long values. |

### `connect_nodes`

Connect output parameters of one node to input parameters of another — one edge, or many at once via the `edges` array. Use source_handle and target_handle to specify exactly which parameters to connect. Handles can be omitted only when the match is unambiguous (a type match, or a single possible pairing) — ambiguous connections return an error listing the available handles.

Common handles and their accepted types:
- promptNode inputs: "prompt" (the model's task/instruction text), "messages" (context, prior outputs, images — accepts any type; don't funnel everything here when a more specific handle fits) | outputs: "messages" (the conversation), "artifacts" (structured output when output_format is set, or extracted code blocks)
- codeNode inputs: "code" (string) | outputs: "result", plus any outputs declared at creation (create_code_node `outputs`)
- inputNode outputs: "content" (matches the input format)
- imageNode inputs: from get_model_params (e.g. prompt, image_url, first_frame_url) | outputs: "image"

You cannot connect to hidden parameters (instruction, format).

An edge overwrites the target input's whole value, so do not also rely on a {{nodeId.param}} template in that same input — the template text would be discarded (the result warns you when this happens). Use one channel per input.

codeNode custom outputs: declare them at creation with create_code_node's `outputs` so they are connectable immediately. A codeNode created WITHOUT declared outputs that returns an object only exposes per-key handles after it has been run.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `source_node_id` | string | no | The ID of the source node (data flows from here) |
| `target_node_id` | string | no | The ID of the target node (data flows to here) |
| `source_handle` | string | no | The output parameter name on the source node (e.g. 'content', 'image', 'messages'). Specify this when the source has multiple outputs or when auto-matching would be ambiguous. |
| `target_handle` | string | no | The input parameter name on the target node (e.g. 'prompt', 'image_url', 'first_frame_url'). Specify this when the target has multiple inputs or when auto-matching would be ambiguous. |
| `edges` | object[] | no | Connect MULTIPLE edges in one call (preferred when wiring up a workflow). Each item is one source→target connection with the same fields as a single connect_nodes call (source_node_id [required], target_node_id [required], source_handle, target_handle). When `edges` is given, the top-level fields are ignored and a per-edge result array is returned — an edge that fails is reported individually without aborting the others. |

### `update_node`

Update an existing node's label, model, output format, input values, position, or lock state. To change a promptNode's structured output use the `output_format` parameter — NOT an input. On artifactNodes, the settings operation_mode, write_behavior, auto_publish, and visibility can be set via `inputs` — they are routed to the node's config. Do NOT use this to edit code in codeNodes — use edit_file (after read_files) instead.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_id` | string | yes | The ID of the node to update (from get_canvas or the creation tool that made it) |
| `label` | string | no | New display label for the node |
| `model_id` | string | no | Change the model on this node (api_id from the AVAILABLE MODELS catalog). Works for promptNode, codeNode, and imageNode. |
| `output_format` | string | no | promptNode only: change the output contract. Pass "list" (array of strings on 'artifacts'), "html" (complete HTML document on 'artifacts'), "code" (single code block on 'artifacts'), a JSON schema string for custom structured output, or "none" to return to plain text. Same semantics as create_prompt_node's output_format. The node must be re-run for the new format to take effect. |
| `inputs` | object | no | Input values to set, keyed by parameter name (use read_nodes to find valid names). Values may be plain strings or arrays of strings. Example: { "prompt": "Summarize the input" }. For codeNodes, a key that does not yet exist creates a NEW named input handle on the node (referenceable in code and connectable). For promptNodes, keys must match existing inputs — model settings (e.g. reasoning_effort) are routed to the model config, and unknown keys return an error listing valid names; use the output_format parameter (not an input) to change structured output. For other node types, keys must match existing input parameters. For select-format inputNodes, pass an object: { "input": { "data": ["opt1", "opt2"], "selected": "opt1" } } — `data` updates the options, `selected` the chosen value; pass only what you want to change. |
| `input_depths` | object | no | Optional matching mode per input parameter, keyed by parameter name: "each" (one run per item), "all" (whole list as one grouped value in a single run), "flatten" (unwrap one nesting level). Numeric equivalents 0/1/-1 are also accepted. Example: { "prompt": "all" }. The default is per-input, NOT always "each": promptNode's "messages" and array-typed model params default to "all". Current values are visible as `depth` in read_nodes. |
| `publish_params` | string[] | no | imageNode only: model parameter names to expose as connectable input edges (e.g. ["num_images", "seed"]) so an upstream node can drive them parametrically. By default a model param the agent sets only lives in the node config/settings bar; publish it here when it must be wired in the workflow. Each published param is seeded from its current config value — combine with `inputs` in the same call to set that value first. |
| `position` | object | no | Move the node. PREFER the relative form ({right_of|left_of|below|above: nodeId, gap?}) — positioned server-side from the anchor's real size. The raw form ({x,y}) is offsets in canvas units from the TOP-LEFT of the user's current viewport (canvas origin when the viewport is unknown). Omit to leave the node where it is. |
| `locked` | boolean | no | Lock (true) or unlock (false) the node. A locked node is pinned: it won't recompute when its inputs change and run_nodes skips it, so its current output is frozen — use this to protect example/reference nodes whose values must not change. Unlocking returns it to the normal (unset) state so it can run again. |

### `organize_nodes`

Auto-layout nodes on the canvas using a topological organize algorithm. Arranges nodes neatly based on their connections while avoiding overlap with other nodes. Call this after all node creation and connect_nodes operations, not in between. Pass the IDs of the nodes you created — the set automatically grows to every node connected to them, so whole workflows stay coherent. Nodes the user has positioned manually are never moved: they act as fixed anchors the rest of the layout arranges itself around (the result reports how many were pinned).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_ids` | string[] | yes | Array of node IDs to organize. Pass the IDs of nodes you created or want to re-layout; anything connected to them is included automatically. |

### `run_nodes`

Execute one or more nodes on the canvas. Nodes run in dependency order — upstream nodes execute first. Code nodes execute server-side in a sandbox when run this way. Returns per node: status (success/error) and, for each output handle, the item count and type actually produced — use these counts to verify data matching did what you expected instead of guessing. Runs whose estimated cost would push this turn's credit spend past the auto-run budget are NOT executed: the call returns status "approval_required" with the estimate — get the user's approval, then re-call with approved_credits.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_ids` | string[] | yes | Array of node IDs to execute. Nodes run in dependency order. |
| `approved_credits` | number | no | Only after a previous call returned status "approval_required" AND the user has explicitly approved the cost: set this to the estimated_credits value from that response to run anyway. Never pass it without the user's approval in this conversation. |

### `delete_nodes`

Delete one or more nodes from the canvas. Also removes any edges connected to the deleted nodes. Use this to clean up nodes that are no longer needed.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_ids` | string[] | yes | Array of node IDs to delete. |

### `delete_edges`

Delete one or more edges (connections) from the canvas. Use this to disconnect nodes without deleting them.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `edge_ids` | string[] | yes | Array of edge IDs to delete (from get_canvas or connect_nodes). |

---

## Tool Discovery

### `search_tools`

Search the library of published Runchat tools (and your own saved runchats) by keyword. Returns matching tools with their runchat_id, name, description, and their input/output parameter names — enough to run a tool straight away with execute_tool. You can also pass the runchat_id to place_tool to put it on the canvas — collapsed by default, or with expand:true to copy its editable nodes for adapting — or to inspect_tool to read how it's built. Call this whenever you need a capability you don't already have built in — web/image search, scrapers, data tools, specialised generators — instead of assuming it doesn't exist.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | no | Keywords to match against tool names and descriptions (e.g. 'web search', 'remove background', 'pdf to text'). Omit to list popular tools. |
| `limit` | number | no | Max results to return. Defaults to 8, max 25. |

### `inspect_tool`

Inspect a published tool / runchat to see how it is built. Returns the published inputs/outputs (names + precise handles — the contract for execute_tool and app UIs) plus a digest of the internal nodes (models, prompts, code) and how they connect. Omit runchat_id to inspect the CURRENT canvas's published tool — e.g. when asked to build an app for "this workflow". You do NOT need it to run a tool (use execute_tool), nor to adapt one — to adapt, call place_tool with expand:true to copy the tool's editable nodes onto the canvas, then change them.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `runchat_id` | string | no | The runchat_id of the tool to inspect (from search_tools). Omit to inspect the current canvas's own published tool. |

### `execute_tool`

Run a published tool / runchat directly and get its outputs back, without placing it on the canvas. Use this to call a capability you found with search_tools (e.g. run a web search, generate or process an image, fetch data) as part of your own reasoning — call it straight after search_tools, no need to inspect_tool first. Provide inputs keyed by the tool's input parameter names (search_tools returns these for each tool); the result reports any inputs that didn't match. Spends credits and requires 'run' permission. Prefer this over place_tool when you only need the result, not a node the user keeps on the canvas.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `runchat_id` | string | yes | The runchat_id UUID of the tool to run (from search_tools or publish_runchat). NOT the /t/... page slug — slugs change on every re-publish (they are resolved as a fallback, but never bake one into app HTML). |
| `inputs` | object | no | Input values keyed by the tool's input parameter names (from inspect_tool). Each value can be a string or an array of strings. Example: { "query": "eames lounge chair" }. When SEVERAL published inputs share a name (e.g. three input nodes all called "input"), an array distributes across them positionally in the order inspect_tool lists them, or use the precise `handle` key ("param_nodeId") per input. Outputs come back keyed BOTH ways: by each output's exact handle (its own value) and by base name (same-named outputs concatenated into one array in listed order). |
| `version_num` | number | no | Optional specific published version to run. Defaults to the latest released version. |

---

## Code Editing Tools

### `edit_file`

Edit one file in a code node. Two modes: (1) targeted edit — pass `old_text` and `new_text`; old_text must match exactly (including whitespace) and must be unique in the file; new_text replaces it. Empty new_text deletes the matched span. (2) Full overwrite — pass only `new_text` (omit old_text); replaces the entire file. ALWAYS read_files first to see current content. When you call this tool, do NOT also output the full file content in your message — the tool applies the change directly.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_id` | string | yes | The ID of the code node. |
| `file` | string | yes | The file path to edit (e.g. 'index.ts', 'index.js', 'package.json'). Use read_files first to discover available paths. |
| `old_text` | string | no | Optional. When present: the exact substring to find and replace. Must be unique in the file — include surrounding context to disambiguate. When omitted: the entire file is overwritten with new_text. |
| `new_text` | string | yes | Required. The replacement text (when old_text is set) or the full new file contents (when old_text is omitted). |

### `create_files`

Create one or more new files in a JavaScript or html code node. JavaScript nodes: extra server modules, package.json for npm dependencies, a static index.html asset — adding files upgrades the node to the bundler sandbox automatically. Html nodes: browser files — ES modules (e.g. src/main.js), stylesheets, package.json for npm dependencies — referenced from index.html via <script type="module" src="/src/main.js"> and <link rel="stylesheet" href="...">; they are bundled with esbuild and inlined into a single self-contained page at render time (import CSS via <link>, never from JS). Errors if any file already exists; use edit_file to modify existing files. The entry file is created automatically with the node — do not try to create it here.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_id` | string | yes | The ID of the JavaScript code node. |
| `files` | object[] | yes | Files to create. Paths must not collide with the entry file or any existing file. |

### `delete_files`

Delete one or more non-entry files from a JavaScript or html code node. Cannot delete the entry file.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_id` | string | yes | The ID of the JavaScript code node. |
| `files` | string[] | yes | Paths of files to delete. |

---

## Code Context Tools

### `read_files`

Read files from a code node. Returns each file's path, line-numbered content, and length. Pass `files` to read specific paths; omit it to list and read everything (always do this first when you don't know what files exist). JavaScript nodes contain an entry file (index.ts) plus any additional files (package.json, modules, .html assets). Other modes contain a single entry file (e.g. index.html, index.py).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_id` | string | yes | The ID of the code node to read. |
| `files` | string[] | no | Optional. Specific file paths to read. Omit to read all files in the node. |

### `read_status`

Read the current error messages, status messages, iframe preview errors, and worker errors for a code node. Call this when debugging issues or when the user reports a problem.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `node_id` | string | yes | The ID of the code node to check. |

---

## Media Tools

### `get_model_params`

Get full parameter details for one or more models (media or LLM). Returns parameter names, types, and defaults — use these as the `params` keys when creating an imageNode. Pick media model_ids from the AVAILABLE MODELS catalog in the system prompt; LLM ids come from list_models (type 'llm'). Pass model_id for one model or model_ids for multiple. Only call once per model.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `model_id` | string | no | A single api_id of the model (from the AVAILABLE MODELS catalog or list_models) |
| `model_ids` | string[] | no | An array of api_ids to fetch details for multiple models at once |

### `list_models`

List the models available to the user. Use type 'llm' to see language models for create_prompt_node's model_id beyond the common picks in the AVAILABLE MODELS catalog. Type 'media' (default) lists generation models (image/video/audio/3d), which the catalog already covers, so only call it to re-check. Returns model_id, name, modality, tags, and cost per model.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | `media` | `llm` | `all` | no | 'media' (default) = generation models. 'llm' = language models for promptNodes. 'all' = both. |
| `query` | string | no | Optional case-insensitive filter on model name, id, or tags. |
| `modality` | string | no | Optional modality filter (e.g. image, video, audio, 3d, llm). |

---

## Vision Tools

### `view_image`

See an image, webpage, or html node render. Pass `url` to fetch an image so you can see and analyze it — use this for images produced by tool results that you haven't seen yet, NOT for raster images already visible in the conversation (e.g. PNG/JPEG the user sent or attached). EXCEPTION: SVG images can't be viewed directly — if an attached or referenced image is an SVG, use this tool to render it to PNG (to see it) or read its source (to edit the markup). If the URL is a WEBPAGE rather than an image, a headless browser captures a viewport screenshot for you — use that to check a page's visual appearance, but use fetch_page to READ a page's text content or links. Pass `node_id` instead to screenshot an html CODE NODE's rendered preview — html nodes execute in the user's browser, so this is the only way to SEE what your HTML/Three.js actually renders; use it after creating or editing an html node to verify the output. If you cannot see images yourself, pass `question` — a vision model answers it and the answer is returned as text. Models that CAN see images always get the image attached to answer themselves.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | no | The URL of the image to view. Supports PNG, JPEG, GIF, WebP, and SVG formats. Pass either url or node_id, not both. |
| `node_id` | string | no | The id of an html code node on the canvas. Its current document (connected inputs injected, same as the canvas preview) is rendered in a headless browser and captured — first paint only; interactive state and window.runchat data are not included. |
| `format` | `render` | `source` | no | For SVG URLs only: 'render' (default) rasterises the SVG to a PNG so you can see it; 'source' returns the raw SVG XML markup so you can read or edit it. Ignored for raster images. |
| `question` | string | no | Optional. A specific question about the image (e.g. "Does this contain a person?", "Does the render meet the brief: modern kitchen, warm lighting?"). Only used when your own model can't view images: a vision model inspects the image and its answer is returned as text. If your model supports images, the image is attached as normal and you answer the question yourself. |

---

## Web Tools

### `web_search`

Search the web and get title/url/snippet results. Use this whenever you need to find pages, facts, or current information. Costs 10 credits per call. For reference IMAGES (visual precedent, style/material references) use search_reference_images instead — it returns direct image URLs, costs a tenth as much, and never hits bot-walls. For other specialised search (academic papers, site crawling, schema extraction), use search_tools to find a dedicated tool.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | yes | The search query. Use a few specific keywords. |
| `count` | number | no | Number of results to return (1-20, default 8). |
| `freshness` | `pd` | `pw` | `pm` | `py` | no | Only return results from the past day (pd), week (pw), month (pm), or year (py). Omit for all time. |

### `search_reference_images`

Search for real reference photographs and get direct image URLs (with title, source, page url, and photographer credit). This is the default way to find visual precedent — style, material, lighting, and mood references for image generation — and normally costs 1 credit per call (web_search costs 10 and returns pages, not images). Pick sources to match the subject; results interleave across them. Returned image URLs can be used directly as image inputs (image_urls, create_input_node) or viewed with view_image. For images that must come from one SPECIFIC website, browse instead: fetch_page that site and view_image candidates.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | yes | What the photo should show. A few concrete keywords (subject, material, light) work better than long sentences. |
| `count` | number | no | Total number of results to return (1-12, default 6). |
| `sources` | string[] | no | Which sources to search. Default ['unsplash','pexels'] — free-license stock photography, 1 credit. Use ['archdaily','divisare','unsplash'] for architecture/interior/landscape (professional project photography with architect + photographer credits). Add 'brave' for web-wide image search — the only source that finds SPECIFIC named things (a product, a named building or furniture piece) — but including it bills the call as a 10-credit search, so only when stock sources won't have the subject. |

### `search_docs`

Search the Runchat documentation (docs.runchat.com) and get the matching sections' text back, with their urls. Free. This is the authoritative source on how Runchat itself works — use it BEFORE answering any question about the product (how to publish a tool, what a node type or input depth does, plugins, MCP, API keys, billing, keyboard shortcuts) and whenever the user seems stuck on the UI, instead of guessing or paying for a web_search. Follow a result's url with fetch_page when you need the whole page.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | yes | What to look up, in a few specific keywords (e.g. 'publish workflow as tool', 'rhino plugin install'). |
| `count` | number | no | Number of doc sections to return (1-10, default 5). |

### `fetch_page`

Fetch a webpage in a headless browser (JavaScript is executed) and return its content. Default format is markdown — the page's readable text with inline links you can follow with further fetch_page calls. Use 'links' to get just the page's outbound links (for navigating a site), or 'html' for the raw rendered HTML (verbose — only when you need markup). Long pages are returned in windows: a truncated result includes next_offset — pass it as offset to get the next part. To check a page's visual appearance instead, use view_image on the page URL for a screenshot.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | yes | The http(s) URL of the page to fetch. |
| `format` | `markdown` | `html` | `links` | no | What to return: 'markdown' (default, readable text content), 'links' (outbound links with anchor text), or 'html' (raw rendered HTML). |
| `offset` | number | no | Character offset to continue reading a long page from — use the next_offset value returned by a previous truncated call. Defaults to 0 (start of page). |
