---
title: "Read board (browser shape)"
method: GET
path: "/api/boards/{boardId}"
tags: ["Boards"]
---

# Read board (browser shape)

`GET /api/boards/{boardId}`

## Request

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

Returns the raw board contents as served to the browser client. Differs from `/json/{id}` in two ways: (1) no `boardId` wrapper — just `{texts, lines, images}`; (2) `images[].dataUrl` carries the FULL base64 payload (not elided). For AI consumption prefer `/json/{id}` — it mirrors the MCP `get_board` shape and elides large image payloads.

**Security note**: like every endpoint in this spec this is served with `Access-Control-Allow-Origin: *`, so any cross-origin page that knows the board id can fetch the full payload (including every `images[].dataUrl`). The board id is the access credential — keep board URLs private for sensitive content, or use `/json/{id}` if you want a lighter snapshot.

## Response `200`

Either a live snapshot (`{texts, lines, images}`) or a tombstone marker (`{deleted: true, deleted_at}`) if the board was erased within the last 30 days.

- union
  - BoardBrowserSnapshot: Browser-client shape returned by `/api/boards/{id}`. Image payloads are NOT elided here.
    - `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` ImageNodeFull[], 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
      - `dataUrl` string: Full `data:image/...;base64,...` payload. Can be up to ~900 kB per image.
  - object
    - `deleted` true, required
    - `deleted_at` string, required: SQLite-formatted UTC timestamp.

## Other responses

- `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)
