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.
https://cnvs.appAccess-Control-Allow-Origin: * on every public endpoint.X-Board-Key header.[A-Za-z0-9-].https://cnvs.app/#<id> is client-side routing — the server never sees the #. For programmatic access use /json/<id> or /api/boards/<id>.GET /json/<id> — JSON snapshotFull 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 previewCompact schematic SVG render (a few kB). Consumable as an image by multimodal LLMs.
GET /api/boards/<id> — browser snapshotHeavier than /json/<id> — images include full base64 payload.
GET /api/boards/<id>/wait?timeout_ms=25000 — long-pollBlocks until the next debounced edit burst or timeout. Returns {updated, timedOut, etag}. REST equivalent of MCP wait_for_update.
POST /api/boards — createReturns {id}. Rate-limited to 5 boards / 60 s / IP.
curl -X POST https://cnvs.app/api/boards
{"id":"a1b2c3..."}
DELETE /api/boards/<id> — soft-deleteTombstones the ID for 30 days so stale links show an "erased" overlay.
POST /api/boards/<id>/texts — add / update textBody: { id?, x, y, content, color?, width?, postit?, author? }. Supports Markdown, Mermaid (one block per node), task-list checkboxes.
POST /api/boards/<id>/strokes — draw strokeBody: { id?, points, color?, author? }. Points accept nested [[x,y],...], flat [x1,y1,...], or JSON string of either.
POST /api/boards/<id>/images — paste imageBody: { 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 capsuleBody: { id?, x, y, url, author? }.
POST /api/boards/<id>/<kind>/<itemId>/moveReposition any item. kind ∈ texts | 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.
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.
GET /openapi.json — OpenAPI 3.1 spec, full schemas + examples.GET /quotas.json — live machine-readable quota values.GET /.well-known/mcp.json — MCP discovery document.GET /llms.txt — LLM-friendly site index.GET /llms-full.txt — full LLM reference.# 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"