---
title: "Board snapshot (MCP get_board shape)"
method: GET
path: "/json/{boardId}"
tags: ["AI-facing"]
---

# Board snapshot (MCP get_board shape)

`GET /json/{boardId}`

## Request

- Base URL: `https://cnvs.app`
- URL: `https://cnvs.app/json/{boardId}`
- Auth: none declared

Full structured JSON snapshot of a board, identical in shape to the MCP `get_board` tool. Includes every text node's COMPLETE content (Mermaid source stays intact inside the `content` field), every stroke's point array, and image metadata. Heavy image payloads (>8 kB base64) are elided (`dataUrl: null`); tiny images (small SVGs, icon-sized PNGs) ride along inline so AI clients can render them without a second round-trip.

The `boardId` can be supplied as a trailing path segment OR as `?board=...` query param — the query form accepts a raw UUID, a hash URL like `https://cnvs.app/#<id>`, or any URL whose path tail is the id.

**Caching for pollers**: every response carries a weak `ETag` header derived from the board's last-updated timestamp + per-kind item counts. Send it back as `If-None-Match` and unchanged boards short-circuit to `304 Not Modified` (empty body, no rate-limit burn). `HEAD` is also supported for cheap existence/freshness probes.

## Headers

- `If-None-Match` string

## Response `200`

Board snapshot.

- BoardSnapshot: Full board state as returned by `/json/{id}` and the MCP `get_board` tool. Same shape as the `cnvs://board/{id}/state.json` MCP resource.
  - `boardId` string, required
  - `mode` 'draw' | 'todo': Board mode. `draw` (default) = infinite-canvas whiteboard; `todo` = kanban task board. Switchable whenever the board is empty of real content (drawings + tasks); empty/seeded columns don't count, so a cleared board can switch again.
  - `texts` TextNode[], required
    - `id` string, required
    - `x` number, required: World x (+x right).
    - `y` number, required: World y (+y DOWN, standard SVG).
    - `content` string, required: Raw text content (up to 100 000 chars). May include Markdown, Mermaid, emoji.
    - `color` string, nullable: Resolved ink value stored on the node — the palette hex for a named color (e.g. `red` → `#ff3b30`) or the CSS variable `var(--text-color)` for theme-aware ink. Writes accept only the color NAMES, never raw hex.
    - `width` number, nullable: Explicit wrapping width in px, or null for auto.
    - `postit` 0 | 1: 1 renders as a yellow sticky note.
    - `kind` 'text' | 'link': `text` is a free-form markdown/Mermaid node; `link` is a URL capsule rendered as a clickable pill. Both live in the same table — `kind` lets machine clients distinguish them without guessing from content.
    - `author` string, nullable: Author tag: `user:<uuid>` for browser edits, `ai:<label>` for MCP edits. IMMUTABLE after creation — subsequent moves / edits by other collaborators do NOT change this value.
    - `last_updated` string: UTC timestamp.
  - `lines` StrokeLine[], required
    - `id` string, required
    - `points` array[], required: Array of `[x, y]` pairs in board world coordinates.
      - number[]
    - `color` string, nullable
    - `author` string, nullable
    - `last_updated` string
  - `images` ImageNode[], required
    - `id` string, required
    - `x` number, required
    - `y` number, required
    - `width` number, required
    - `height` number, required
    - `thumbDataUrl` string, nullable: Tiny preview embedded in SVG previews (≤8 kB).
    - `author` string, nullable
    - `last_updated` string
  - `columns` Column[]: Kanban columns (present / non-empty on `todo`-mode boards).
    - `id` string, required
    - `title` string, required
    - `sort` number: Left-to-right order of the column.
    - `lane` integer: Row index for multi-lane layout. Defaults to 0 (top row).
    - `color` 'red' | 'blue' | 'green' | 'null', nullable: Column title color — one of `red` / `blue` / `green`, or null for the default.
    - `author` string, nullable
    - `last_updated` string: UTC timestamp.
  - `tasks` Task[]: Kanban tasks (present / non-empty on `todo`-mode boards).
    - `id` string, required
    - `column_id` string, required: Column the card currently lives in.
    - `name` string, required: Card title.
    - `content` string, nullable: Opaque JSON string blob, e.g. `{"description":...}`. Treat as a blob — do not parse application meaning out of it. Max 20 000 chars.
    - `due_date` string, nullable: ISO 8601 due date, or null.
    - `priority` 'H' | 'M' | 'L' | 'null', nullable: High / Medium / Low, or null.
    - `assignee` string, nullable
    - `done` boolean: Whether the task is completed (checked off). Defaults to false.
    - `sort` number: Order within the column.
    - `author` string, nullable
    - `last_updated` string: UTC timestamp.

## Other responses

- `304`: Unchanged since the supplied `If-None-Match`. Empty body.
- `400`: Invalid input (missing or malformed board id).
- `401`: Board is access-locked and the request did not carry a valid key. `lockMode` tells the client whether to prompt for a code or just disable write UI.
- `404`: Board does not exist or was deleted within the 30-day tombstone window.
- `429`: Rate limit exceeded. Standard REST/MCP: 60 requests/10s/board; valid X-Import-Token infrastructure tier: 600/10s/board; browser WebSocket always 60/10s/board. REST/MCP enforcement is soft per-isolate; WebSocket enforcement is strong per Durable Object.

---

[API](https://skmtc.dev/cnvs/apis/cnvs-app-http-api.md) · [All operations](https://skmtc.dev/cnvs/apis/cnvs-app-http-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cnvs/apis/cnvs-app-http-api/revisions/c8f2c3b7ff90?raw)
