cnvs.app REST API — full reference

Every MCP tool has a 1:1 REST mirror. REST is universal — any HTTP client with outbound network can use it without an MCP SDK. Same server-side validator, same WebSocket broadcast, same per-board quotas.

OpenAPI 3.1 spec → LLM-friendly full reference

Base URL & conventions

Reads

GET /json/<id> — JSON snapshot

Full structured JSON, same shape as MCP get_board. Includes Mermaid source inside text content. ETag-aware: send If-None-Match for 304 on no-change. Also accepts ?board=<url-or-id>.

HEAD /json/<id>

Same headers (incl. ETag) as GET, zero body. Cheap freshness probe.

GET /svg-preview/<id> — SVG preview

Compact schematic SVG render (a few kB). Consumable as an image by multimodal LLMs.

GET /api/boards/<id> — browser snapshot

Heavier than /json/<id> — images include full base64 payload.

GET /api/boards/<id>/wait?timeout_ms=25000 — long-poll

Blocks until the next debounced edit burst or timeout. Returns {updated, timedOut, etag}. REST equivalent of MCP wait_for_update.

Boards

POST /api/boards — create

Returns {id}. Rate-limited to 5 boards / 60 s / IP.

curl -X POST https://cnvs.app/api/boards
{"id":"a1b2c3..."}

DELETE /api/boards/<id> — soft-delete

Tombstones the ID for 30 days so stale links show an "erased" overlay.

Mutations (1:1 with MCP tools)

POST /api/boards/<id>/texts — add / update text

Body: { id?, x, y, content, color?, width?, postit?, author? }. Supports Markdown, Mermaid (one block per node), task-list checkboxes.

POST /api/boards/<id>/strokes — draw stroke

Body: { id?, points, color?, author? }. Points accept nested [[x,y],...], flat [x1,y1,...], or JSON string of either.

POST /api/boards/<id>/images — paste image

Body: { id?, x, y, width, height, dataUrl, thumbDataUrl?, author? }. dataUrl must be a data:image/(png|jpeg|gif|webp|svg+xml);base64,... string, ≤ ~900 kB.

POST /api/boards/<id>/links — drop URL capsule

Body: { id?, x, y, url, author? }.

POST /api/boards/<id>/<kind>/<itemId>/move

Reposition any item. kindtexts | links | strokes | images. Body: { x, y }.

DELETE /api/boards/<id>/<kind>/<itemId>

Erase any item. Unknown kinds return 400 (no silent mis-targeting).

Default author for REST mutations: ai:rest. Override via author: "ai:<label>". The author tag is immutable after creation.

Per-board lock

POST /api/boards/<id>/lock {mode}

Returns the 6-char key ONCE on first lock. Mode: "write" or "all".

POST /api/boards/<id>/unlock (requires X-Board-Key)

POST /api/boards/<id>/verify-key {key}

Locked boards reject mutations / reads (depending on mode) until the key is supplied via X-Board-Key header. No recovery: lose the key, lose the board.

Metadata & discovery

Quick end-to-end example

# 1. Create a board
ID=$(curl -s -X POST https://cnvs.app/api/boards | jq -r .id)

# 2. Add a text node
curl -X POST "https://cnvs.app/api/boards/$ID/texts" \
  -H "Content-Type: application/json" \
  -d '{"x":200,"y":160,"content":"Hello from REST!","postit":true,"author":"ai:my-bot"}'

# 3. Draw a stroke
curl -X POST "https://cnvs.app/api/boards/$ID/strokes" \
  -H "Content-Type: application/json" \
  -d '{"points":[[100,100],[200,150],[300,100]],"color":"red"}'

# 4. Read the snapshot
curl -s "https://cnvs.app/json/$ID" | jq .

# 5. Open in browser
echo "https://cnvs.app/#$ID"

Related