Save a whiteboard sketch on a post
POST /posts/{id}/whiteboards
Persists a brush-sketch as a top-level comment with
annotation_type='whiteboard'. The content payload carries
the strokes (vector source of truth — perfect-freehand input
points + per-layer arrangement) plus the source canvas
dimensions so the viewer can re-render proportionally
regardless of viewport size.
Requires posts.comment capability (Base default) — same
gate as posting a regular comment, since whiteboards ARE
comments at the storage layer. Future phases can split the
capability if finer governance is needed.
title is optional. If omitted the sidebar list shows
“Untitled sketch” with the author + timestamp.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Request Body required
Section titled “Request Body required ”object
Optional title for the sidebar list.
Vector source of truth for a whiteboard / annotation sketch. The frontend renders from this; OCR / AI ingestion later can consume it directly without going through the raster snapshot.
object
Width of the source canvas in CSS pixels at save time. Used to scale the drawing proportionally when re-rendered in a different viewport size — strokes are stored in source-canvas coordinates, not viewport pixels.
Optional content-addressed hash of a rasterized PNG snapshot in object storage. Convenience output for OCR / AI / PDF export later. Null on save means strokes are authoritative and the snapshot will be lazily rendered.
Canvas background color (C-1.18). Hex string painted behind every layer before items render. Defaults to ‘#ffffff’ when missing so saves from C-1.0..C-1.17 render identically.
Ordered bottom-to-top. Each layer carries its own visible / opacity / lock / items payload — same shape Photoshop / Procreate use. Capped at 32 to keep payloads bounded.
object
Stable per-layer identifier (UUIDv4 from the client).
When true the layer is read-only; visibility stays toggleable.
Polymorphic items on this layer. kind is the
discriminator: stroke (free-form brush), shape
(line / arrow / rect / ellipse), text (inline
label), or image (pasted/dropped bitmap, stored
inline as a base64 data: URL for now).
object
Legacy single color (still set on shape items for back-compat). Stroke + text items use this exclusively; shape items prefer strokeColor / fillColor when present.
Shape outline color (C-1.17). Falls back to color when missing.
Shape fill color (C-1.17). Falls back to color when missing.
Shape fill opacity. 0 = outline only, 1 = solid fill.
Base64 data: URL OR remote URL. Pasted images
are embedded as data URLs (5 MB cap enforced
client-side); content-addressed image_hash
reference lands in a later phase.
Responses
Section titled “ Responses ”Whiteboard saved
A single comment in a thread. Threading is via parent_id + root_id + depth (caller renders nested replies indented by depth). annotation_* fields are NULL for plain comments; the review-mode feature reuses the same row shape.
Federation: exactly one of {author_user_ref} XOR {peer_id + actor_uri} is set per row. Local-authored comments have author_user_ref set + peer_id NULL. Remote-authored comments (from a paired peer per ADR 0043 / phase 1.22.D) have author_user_ref NULL + peer_id + actor_uri + display_name populated. Clients render “<display_name> @ <peer’s host>” for remote rows.
object
Local author user.ref. NULL for remote-authored comments (see peer_id / actor_uri).
Server-rendered safe HTML; clients should render this rather than reformatting body.
object
The federation peer that originated this comment. NULL for local-authored rows. Set in tandem with actor_uri
- display_name when the comment came in via the federation inbox.
The remote actor’s URI on their home instance (e.g. https://studio-b.example/users/bob). NULL for local-authored rows.
Display name resolved from federation_remote_actors cache for remote-authored comments. May be empty if the remote instance hasn’t shipped display hints yet; clients should fall back to the actor_uri’s host + local-part when this is empty.
Malformed request
object
Human-readable error summary
Example
the request could not be completedAuthentication required, missing, or invalid
object
Human-readable error summary
Example
the request could not be completedExample
{ "error": "authentication required: sign in and retry with a valid session or API token"}Authenticated but missing required capabilities
object
Human-readable error summary
Example
the request could not be completedResource not found
object
Human-readable error summary
Example
the request could not be completed