A canvas board is JSON Canvas and a drawing is Excalidraw. The tools here build and change those files from a compact spec. You send a node list or Mermaid source and the server writes the JSON. Every call lands in version history like any other write. The request envelope is the same as every other tool, see [[Calling Tools]].

```bash title="Terminal"
curl -X POST "https://www.monofile.ai/api/tools/checkpoint" \
  -H "Authorization: Bearer $MONOFILE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path": "/Projects", "name": "before refactor"}'
```

## Canvas boards

A `.canvas` file is a spatial board of nodes and edges, described in [[Canvas Boards]].

> [!WARNING]
> Do not use `write` or `edit` on a `.canvas` file. String editing corrupts the JSON, and a corrupt board will not open. Use the three tools below instead.

Both write tools take the same node and edge fields:

| Node field | Applies to | Description |
|---|---|---|
| `type` | all | `text`, `file`, `link` or `group` |
| `ref` | all | Name this node so later ops can point at it as `$name`. Letters, digits and underscores, up to 32 characters |
| `x`, `y`, `width`, `height` | all | Position and size. Omit them and let auto-layout place the node |
| `color` | all | Node color |
| `text` | `text` | Markdown content of a note |
| `file` | `file` | Workspace-relative path, for example `screens/home.html` |
| `content` | `file` | Write that file with this content in the same call. An HTML file node renders live as an artboard at the node size |
| `subpath` | `file` | A `#heading` inside the file |
| `url` | `link` | The link target |
| `label` | `group` | Caption of a group frame |

| Edge field | Description |
|---|---|
| `fromNode`, `toNode` | Node ids, or `$n` and `$ref` references to nodes created in the same call |
| `fromSide`, `toSide` | Which side of the node the edge leaves or enters |
| `fromEnd`, `toEnd` | Arrow ends |
| `color`, `label` | Appearance and caption |

### create_canvas

Creates a board in one call, with its nodes, the edges between them and, if you ask, a layout pass that positions everything.

| Input | Type | Required | Description |
|---|---|---|---|
| `path` | string | yes | Path ending in `.canvas` |
| `nodes` | array | no | Up to 200 node specs |
| `edges` | array | no | Up to 200 edge specs |
| `layout` | string | no | Auto-layout direction: `RIGHT`, `DOWN`, `LEFT` or `UP`. Recommended whenever you omit `x` and `y` |
| `overwrite` | boolean | no | Replace an existing file instead of failing with `FILE_EXISTS` |

Scope: `files:write`. The response carries `createdNodeIds`, `createdEdgeIds` and a compact node map you can use for follow-up calls.

```http
POST /api/tools/create_canvas
{
  "path": "/boards/pipeline.canvas",
  "nodes": [
    { "type": "text", "ref": "intake", "text": "## Intake\nForms land here" },
    { "type": "text", "ref": "review", "text": "## Review" },
    { "type": "file", "file": "Projects/spec.md" }
  ],
  "edges": [{ "fromNode": "$intake", "toNode": "$review", "label": "queued" }],
  "layout": "RIGHT"
}
```

### canvas_edit

Applies a batch of operations to a board in order, creating the file if it does not exist. The batch is atomic. If any operation fails, nothing is written.

| Input | Type | Required | Description |
|---|---|---|---|
| `path` | string | yes | Path ending in `.canvas` |
| `ops` | array | yes | 1 to 200 operations, applied in order |

| Operation | Shape |
|---|---|
| `add_node` | The node fields above |
| `update_node` | `{ "id", "set": { fields to change } }`. A `null` value removes a field |
| `delete_nodes` | `{ "ids": [...] }`. Edges touching a deleted node go with it |
| `add_edge` or `connect` | The edge fields above |
| `update_edge` | `{ "id", "set": { ... } }` |
| `delete_edges` | `{ "ids": [...] }` |
| `layout` | `{ "direction", "nodeIds" }`. Omit `nodeIds` to lay out the whole board |

Scope: `files:write`.

Later operations can refer to earlier ones. `$1` is the first creating operation in the batch, `$2` the second, and `$name` is any operation that declared `ref: "name"`. So one call can create a node, connect it to the node it just created, and lay out the result, with no round trip to learn the new id.

```http
POST /api/tools/canvas_edit
{
  "path": "/boards/pipeline.canvas",
  "ops": [
    { "op": "add_node", "ref": "ship", "type": "text", "text": "## Ship" },
    { "op": "connect", "fromNode": "review-node-id", "toNode": "$ship" },
    { "op": "layout", "direction": "RIGHT" }
  ]
}
```

An error names the index of the operation that failed, with one of `INVALID_OP`, `UNKNOWN_REF`, `NODE_NOT_FOUND`, `EDGE_NOT_FOUND`, `MISSING_FIELD`, `INVALID_DOCUMENT`.

### canvas_check

Reports whether a board's file references still point at real files.

| Input | Type | Required | Description |
|---|---|---|---|
| `path` | string | yes | Path to the `.canvas` file |
| `repair` | boolean | no | Rewrite the fixable references in place. Default `false` |

Scope: `files:read`. `repair: true` also needs `files:write`. A read-only key can still inspect a board.

```http
POST /api/tools/canvas_check
{ "path": "/boards/pipeline.canvas" }
```

:::details Status values
| Status | Renders | Meaning |
|---|---|---|
| `ok` | yes | Id and path both match |
| `path_stale` | yes | The file moved and the node's portable path did not. Repairable |
| `path_only` | yes | No stable id, so one rename away from breaking. Repairable |
| `unresolved_id` | no | The id matches nothing here, for example a board forked in from another workspace |
| `missing` | no | Nothing matches the path |
| `section_missing` | no | The file is there, the `#heading` is not |
| `subpath_ignored` | yes | A `#section` on a file that has no sections |
:::

`repair: true` rewrites only `path_stale` and `path_only`. It leaves a node that resolves to nothing alone.

## Slide decks

A `.slides` deck is plain markdown, so `write` and `edit` work on it. Four tools address slides by number or id so you do not have to count separators. They mirror the canvas tools, and the built-in assistant uses the same ones. The format, the inspector and presenting are on [[Slide Decks]].

### create_slides

Creates a deck from a structured spec in one call. Each slide takes `content` (markdown, or HTML with `html: true`), optional `notes`, `layout`, `id`, `background`, `src` and `skip`. Slides without an `id` get one minted. Scope: `files:write`.

```http
POST /api/tools/create_slides
{
  "path": "/decks/pitch.slides",
  "title": "Monofile pitch",
  "theme": "midnight",
  "slides": [
    { "id": "intro", "layout": "title", "content": "# Monofile\nOne filesystem for people and agents", "notes": "Thirty seconds on who we are." }
  ]
}
```

The response carries the slide count, the created ids, a compact slide list and a `contentHash` for the next edit.

### slides_edit

Applies a batch of operations in order, atomically: `insert_slide`, `update_slide`, `delete_slides`, `move_slide` and `set_deck`. Address slides by 1-based number, by `#id`, or by `$n` for a slide inserted earlier in the same batch. Pass the previous `contentHash` as `ifHash` to refuse the write if the deck changed in between. Scope: `files:write`.

```http
POST /api/tools/slides_edit
{
  "path": "/decks/pitch.slides",
  "ops": [
    { "op": "insert_slide", "after": "#intro", "slide": { "id": "next", "content": "## Next steps\n- Ship the desktop beta" } }
  ],
  "ifHash": "1a2b3c4d-k9"
}
```

Inserting a slide whose `id` already exists is a no-op with a warning, so a retry does not duplicate the slide. If any operation fails, nothing is written.

### slides_check

Reports missing or moved references, empty slides, duplicate ids, slides without notes and slides over their word budget. `repair: true` rewrites moved references in place. Scope: `files:read`, plus `files:write` for a repair.

```http
POST /api/tools/slides_check
{ "path": "/decks/pitch.slides", "repair": true }
```

### slides_export

Writes the deck to the workspace as standalone HTML or an editable PowerPoint file. `format` is `html` or `pptx`; `output` defaults to the deck's path with the new extension; `overwrite` replaces an existing output instead of failing with `FILE_EXISTS`. A `.pptx` lands as an attachment file (a download card). Scope: `files:write`.

```http
POST /api/tools/slides_export
{ "path": "/decks/pitch.slides", "format": "pptx", "overwrite": true }
```

## Diagrams

### create_diagram

Turns Mermaid source into an editable Excalidraw drawing and saves it as a `.draw` file. Send standard Mermaid and the server converts it. Do not write Excalidraw JSON by hand.

| Input | Type | Required | Description |
|---|---|---|---|
| `mermaid` | string | yes | Mermaid source, up to 50 KB |
| `path` | string | yes | Path ending in `.draw` |

Scope: `files:write`. Five diagram types convert: flowchart (`graph TD` and `graph LR`, including subgraphs and `classDef` styling), `sequenceDiagram`, `classDiagram`, `erDiagram` and `stateDiagram-v2`. Anything else comes back as `UNSUPPORTED_TYPE`.

```http
POST /api/tools/create_diagram
{
  "path": "/diagrams/login.draw",
  "mermaid": "graph TD\n  A[Start] --> B{Valid?}\n  B -->|Yes| C[Process]\n  B -->|No| D[Error]"
}
```

A syntax error returns `PARSE_ERROR` with the line number, so fix that line and retry. The saved file keeps the Mermaid source alongside the drawing, so you can read it back and regenerate later. See [[Diagrams]] and [[Drawings]].

## Versions and history

Monofile captures every write as a version and groups the writes of one call into a changeset. These six tools read and undo that history. The concepts are in [[How Versioning Works]] and [[Changesets]].

### list_versions

A file's version history, newest first, metadata only.

| Input | Type | Required | Description |
|---|---|---|---|
| `path` | string | yes | Full path to the file |
| `limit` | number | no | 1 to 50, out-of-range values are clamped. Default 20 |

Scope: `files:read`. Each entry carries a `source` (`agent` for an automatic before-image, `user` for a human edit, `checkpoint`, `revert`), an `operation`, and the changeset it belongs to. Start here to get the `versionId` the next two tools need.

```http
POST /api/tools/list_versions
{ "path": "/Projects/report.md", "limit": 10 }
```

### read_version

The content of one previous version. Use it to compare an old version against the current content.

| Input | Type | Required | Description |
|---|---|---|---|
| `path` | string | yes | Full path to the file |
| `versionId` | string | yes | From `list_versions` |

Scope: `files:read`.

```http
POST /api/tools/read_version
{ "path": "/Projects/report.md", "versionId": "<versionId from list_versions>" }
```

### revert_file

Restores a file to a previous version. It saves the current content as a new version first, so you can undo the revert.

| Input | Type | Required | Description |
|---|---|---|---|
| `path` | string | yes | Full path to the file |
| `versionId` | string | yes | The version to restore |

Scope: `files:write`. For undoing a whole multi-file run, use `revert_changeset` instead.

```http
POST /api/tools/revert_file
{ "path": "/Projects/report.md", "versionId": "<versionId from list_versions>" }
```

### checkpoint

A named snapshot of the current content of a file, or of every file under a folder. Monofile does not prune checkpoints automatically, so take one before a large or risky change.

| Input | Type | Required | Description |
|---|---|---|---|
| `path` | string | yes | File or folder path |
| `name` | string | yes | 1 to 60 characters, for example `before refactor` |

Scope: `files:write`. Caps: 10 checkpoints per file, and 200 files per folder checkpoint. See [[Checkpoints]].

```http
POST /api/tools/checkpoint
{ "path": "/Projects", "name": "before refactor" }
```

### list_changesets

Recent changesets, newest first, each with the files it touched and a summary.

| Input | Type | Required | Description |
|---|---|---|---|
| `limit` | number | no | 1 to 50, clamped. Default 10 |
| `path` | string | no | Only changesets that touched this file |

Scope: `files:read`. This is how you find the `changesetId` when someone asks you to undo a previous run.

```http
POST /api/tools/list_changesets
{ "limit": 10, "path": "/Projects/report.md" }
```

### revert_changeset

Undoes an entire changeset. Each file goes back to its content before the run, and files the run created are deleted.

| Input | Type | Required | Description |
|---|---|---|---|
| `changesetId` | string | yes | From `list_changesets` |
| `force` | boolean | no | Revert even if files moved on since. Default `false` |

Scope: `files:write` and `files:delete`. If anyone wrote to a file after the changeset, the call fails with `CONFLICT` and lists what changed. Confirm with whoever owns the work, then retry with `force: true`. Either way the tool captures the pre-revert state first, so you can revert the revert. [[Reverting Safely]] covers when to force.

```http
POST /api/tools/revert_changeset
{ "changesetId": "<changesetId from list_changesets>" }
```

## Next steps

:::cards
- [[Calling Tools]] — The one-endpoint pattern, the MCP-shaped envelope, and the index of every tool.
- [[File and Shell Tools]] — Reading, writing and editing files, plus the two shell tiers.
- [[Changesets]] — Runs grouped into one reviewable, revertable unit.
- [[Canvas Boards]] — Spatial boards of live file nodes, notes, links, groups and labelled edges.
:::
