{"documents":[],"api":{"create_document":{"method":"POST","url":"https://thinkroom.kieranklaassen.com/api/docs","headers":{"X-Agent-Name":"recommended","Content-Type":"application/json"},"success_status":201,"rate_limits":{"by":"source_ip","response_status":429,"burst":{"requests":20,"within_seconds":600},"daily":{"requests":100,"within_seconds":86400}},"body":{"title":"(optional)","format":"markdown | html","content":"(required with explicit format; canonical source)"},"limits":{"content_max_bytes":2097152},"content_contracts":{"markdown":{"version":2,"content_format":"markdown","immutable":true,"canonical_source_field":"content","rendered_text_field":"plain_text","suggestion_body_format":"markdown","editor_model":"Humans edit a rendered document. ProseMirror JSON and Yjs updates are internal and are not accepted by the HTTP API.","normalization":{"response_field":"normalized","warning_field":"warning","meaning":"When normalized is true, unsupported or unsafe source was removed or rewritten, or an excalidraw block was not recognized as a valid sketch and was kept as a code block. The warning field explains what happened."},"sketches":{"purpose":"Inline Excalidraw sketches remain editable in the human UI and expose text semantics to agents.","markdown_source":{"format":"A fenced \"excalidraw\" code block whose body is a single JSON object (the SketchData wrapper). The fence language must be exactly \"excalidraw\".","schema":{"formatVersion":"(required) integer, must equal 1.","id":"(required) string matching /^[a-zA-Z0-9_-]{1,100}$/. Enforced by the editor AND the create-time signal — a missing or invalid id keeps the fence a code block (see enforcement).","description":"(optional) human summary up to 500 characters; surfaced in plain_text.","height":"(optional) integer reserved render height in pixels, default 448; a finite value outside 180-1200 is clamped into that range by the editor and the server preview, so an out-of-range height still renders (clamped) rather than breaking (see enforcement).","scene":{"type":"(required) must equal \"excalidraw\".","version":"(required) integer greater than 0 (the Excalidraw scene version, e.g. 2).","elements":"(required) array of Excalidraw elements. Each element type must be one of rectangle, diamond, ellipse, line, arrow, freedraw, text, frame; any strokeColor/backgroundColor must match /^(?:transparent|#[0-9a-f]{3,8})$/i; fileId and link are not allowed.","appState":"(optional) object; viewBackgroundColor must match the safe-color pattern. Only an allowlist of keys (theme, grid/zoom/scroll settings, viewBackgroundColor) is retained.","files":"(required) must be exactly {} - embedded bitmap files are not supported."}},"example":"```excalidraw\n{\"id\":\"flow1\",\"formatVersion\":1,\"description\":\"Human and AI agent edit the same Yjs room\",\"height\":260,\"scene\":{\"type\":\"excalidraw\",\"version\":2,\"elements\":[{\"id\":\"r1\",\"type\":\"rectangle\",\"x\":100,\"y\":100,\"width\":220,\"height\":90,\"angle\":0,\"strokeColor\":\"#1e1e1e\",\"backgroundColor\":\"#d3f9d8\",\"fillStyle\":\"solid\",\"strokeWidth\":2,\"strokeStyle\":\"solid\",\"roughness\":1,\"opacity\":100,\"groupIds\":[],\"frameId\":null,\"roundness\":{\"type\":3},\"seed\":1,\"version\":1,\"versionNonce\":1,\"isDeleted\":false,\"boundElements\":null,\"updated\":1,\"link\":null,\"locked\":false},{\"id\":\"t1\",\"type\":\"text\",\"x\":130,\"y\":132,\"width\":60,\"height\":25,\"angle\":0,\"strokeColor\":\"#1e1e1e\",\"backgroundColor\":\"transparent\",\"fillStyle\":\"solid\",\"strokeWidth\":2,\"strokeStyle\":\"solid\",\"roughness\":1,\"opacity\":100,\"groupIds\":[],\"frameId\":null,\"roundness\":null,\"seed\":1,\"version\":1,\"versionNonce\":1,\"isDeleted\":false,\"boundElements\":null,\"updated\":1,\"link\":null,\"locked\":false,\"text\":\"Human\",\"fontSize\":20,\"fontFamily\":1,\"textAlign\":\"left\",\"verticalAlign\":\"top\",\"containerId\":null,\"originalText\":\"Human\",\"lineHeight\":1.25,\"baseline\":18}],\"appState\":{\"viewBackgroundColor\":\"#fffef9\"},\"files\":{}}}\n```\n","recognition":"A recognized sketch renders in plain_text as \"Sketch: <description> — <labels>\". If plain_text instead echoes the raw scene JSON, the fence was not recognized; the create response then reports normalized: true with a warning.","enforcement":"The create-time signal (normalized/warning) applies the same recognition rule as the editor: scene shape AND id. A fence with a missing/invalid id is kept as a code block everywhere and the create response reports normalized: true with a warning. Height is different: it is not a recognition criterion, and an out-of-range or non-positive height is clamped into 180-1200 (matching the server preview) so the sketch renders rather than breaking. The stored content keeps the height you submitted; the editor and preview render the clamped value.","reference":"Excalidraw scene/element format and drawing semantics: https://docs.excalidraw.com/docs/codebase/json-schema"},"html_source":"A trusted figure[data-thinkroom-sketch] snapshot; external HTML cannot set reserved sketch attributes.","rendered_context":"plain_text emits the sketch description and text labels instead of raw scene JSON.","canonical":"The Excalidraw scene is canonical; SVG is generated in the browser for preview, copy, and download.","supported_elements":["rectangle","diamond","ellipse","line","arrow","freedraw","text","frame"],"limits":{"scene_max_bytes":524288,"description_max_characters":500,"elements_max":500,"points_max":20000,"embedded_images":false}}},"html":{"version":2,"content_format":"html","immutable":true,"canonical_source_field":"content","rendered_text_field":"plain_text","suggestion_body_format":"html","editor_model":"Humans edit a rendered document. ProseMirror JSON and Yjs updates are internal and are not accepted by the HTTP API.","normalization":{"response_field":"normalized","warning_field":"warning","meaning":"When normalized is true, unsupported or unsafe source was removed or rewritten, or an excalidraw block was not recognized as a valid sketch and was kept as a code block. The warning field explains what happened."},"sketches":{"purpose":"Inline Excalidraw sketches remain editable in the human UI and expose text semantics to agents.","markdown_source":{"format":"A fenced \"excalidraw\" code block whose body is a single JSON object (the SketchData wrapper). The fence language must be exactly \"excalidraw\".","schema":{"formatVersion":"(required) integer, must equal 1.","id":"(required) string matching /^[a-zA-Z0-9_-]{1,100}$/. Enforced by the editor AND the create-time signal — a missing or invalid id keeps the fence a code block (see enforcement).","description":"(optional) human summary up to 500 characters; surfaced in plain_text.","height":"(optional) integer reserved render height in pixels, default 448; a finite value outside 180-1200 is clamped into that range by the editor and the server preview, so an out-of-range height still renders (clamped) rather than breaking (see enforcement).","scene":{"type":"(required) must equal \"excalidraw\".","version":"(required) integer greater than 0 (the Excalidraw scene version, e.g. 2).","elements":"(required) array of Excalidraw elements. Each element type must be one of rectangle, diamond, ellipse, line, arrow, freedraw, text, frame; any strokeColor/backgroundColor must match /^(?:transparent|#[0-9a-f]{3,8})$/i; fileId and link are not allowed.","appState":"(optional) object; viewBackgroundColor must match the safe-color pattern. Only an allowlist of keys (theme, grid/zoom/scroll settings, viewBackgroundColor) is retained.","files":"(required) must be exactly {} - embedded bitmap files are not supported."}},"example":"```excalidraw\n{\"id\":\"flow1\",\"formatVersion\":1,\"description\":\"Human and AI agent edit the same Yjs room\",\"height\":260,\"scene\":{\"type\":\"excalidraw\",\"version\":2,\"elements\":[{\"id\":\"r1\",\"type\":\"rectangle\",\"x\":100,\"y\":100,\"width\":220,\"height\":90,\"angle\":0,\"strokeColor\":\"#1e1e1e\",\"backgroundColor\":\"#d3f9d8\",\"fillStyle\":\"solid\",\"strokeWidth\":2,\"strokeStyle\":\"solid\",\"roughness\":1,\"opacity\":100,\"groupIds\":[],\"frameId\":null,\"roundness\":{\"type\":3},\"seed\":1,\"version\":1,\"versionNonce\":1,\"isDeleted\":false,\"boundElements\":null,\"updated\":1,\"link\":null,\"locked\":false},{\"id\":\"t1\",\"type\":\"text\",\"x\":130,\"y\":132,\"width\":60,\"height\":25,\"angle\":0,\"strokeColor\":\"#1e1e1e\",\"backgroundColor\":\"transparent\",\"fillStyle\":\"solid\",\"strokeWidth\":2,\"strokeStyle\":\"solid\",\"roughness\":1,\"opacity\":100,\"groupIds\":[],\"frameId\":null,\"roundness\":null,\"seed\":1,\"version\":1,\"versionNonce\":1,\"isDeleted\":false,\"boundElements\":null,\"updated\":1,\"link\":null,\"locked\":false,\"text\":\"Human\",\"fontSize\":20,\"fontFamily\":1,\"textAlign\":\"left\",\"verticalAlign\":\"top\",\"containerId\":null,\"originalText\":\"Human\",\"lineHeight\":1.25,\"baseline\":18}],\"appState\":{\"viewBackgroundColor\":\"#fffef9\"},\"files\":{}}}\n```\n","recognition":"A recognized sketch renders in plain_text as \"Sketch: <description> — <labels>\". If plain_text instead echoes the raw scene JSON, the fence was not recognized; the create response then reports normalized: true with a warning.","enforcement":"The create-time signal (normalized/warning) applies the same recognition rule as the editor: scene shape AND id. A fence with a missing/invalid id is kept as a code block everywhere and the create response reports normalized: true with a warning. Height is different: it is not a recognition criterion, and an out-of-range or non-positive height is clamped into 180-1200 (matching the server preview) so the sketch renders rather than breaking. The stored content keeps the height you submitted; the editor and preview render the clamped value.","reference":"Excalidraw scene/element format and drawing semantics: https://docs.excalidraw.com/docs/codebase/json-schema"},"html_source":"A trusted figure[data-thinkroom-sketch] snapshot; external HTML cannot set reserved sketch attributes.","rendered_context":"plain_text emits the sketch description and text labels instead of raw scene JSON.","canonical":"The Excalidraw scene is canonical; SVG is generated in the browser for preview, copy, and download.","supported_elements":["rectangle","diamond","ellipse","line","arrow","freedraw","text","frame"],"limits":{"scene_max_bytes":524288,"description_max_characters":500,"elements_max":500,"points_max":20000,"embedded_images":false}},"html":{"scope":"Semantic body HTML, not a lossless full-page HTML/CSS document.","allowed_elements":["p","h1","h2","h3","h4","h5","h6","blockquote","pre","code","br","hr","ul","ol","li","strong","b","em","i","s","del","a","img","table","thead","tbody","tr","th","td","span","ins","figure","figcaption"],"dropped_with_content":["script","style","iframe","object","embed","template","svg","math"],"attributes":{"supported":["href","title","src","alt","start","style","colspan","rowspan","colwidth","data-language","data-item-type","data-label","data-list-type","data-spread","data-checked","data-is-header"],"reserved":"Thinkroom provenance, suggestion, and sketch data attributes may appear in trusted snapshots; external source cannot set them."},"css":{"supported":"Only text-align: left|center|right on th and td.","removed":["<style> blocks","class and id styling hooks","all other inline style declarations"],"guidance":"Use semantic elements and Thinkroom's editor styling. Do not depend on custom colors, spacing, fonts, grids, or page-level layout."},"images":{"upload":{"method":"POST","url":"https://thinkroom.kieranklaassen.com/api/uploads","headers":{"X-Agent-Name":"required"},"success_status":201,"request":{"content_type":"multipart/form-data","field":"file","identity_header":"X-Agent-Name","allowed_content_types":["image/png","image/jpeg","image/webp"],"max_bytes":3145728,"max_dimension":6000,"max_pixels":20000000},"processing":"Images are decoded and re-encoded; metadata and unsupported payloads are removed."},"embed":"<img src=\"RETURNED_SRC\" alt=\"Descriptive text\">","guidance":"Use the exact src returned by api.upload_image. It expires unless referenced by saved HTML within one hour.","accepted_sources":"Validated same-origin Active Storage blob, representation, or disk paths. Agents should only generate the blob src returned by the upload endpoint.","removed_sources":["https:// remote images","protocol-relative URLs","data: URLs","arbitrary same-origin paths","URLs with query strings or fragments"]}}}},"returns":{"slug":"Document identifier","share_url":"Browser/editor URL","content_format":"Immutable markdown or html","content":"Canonical source","plain_text":"Rendered text","normalized":"Whether source changed during normalization","content_contract":"Machine-readable source, HTML, CSS, and image rules"},"purpose":"Create a new shared document. X-Agent-Name is recommended for seed attribution but creation also permits an unattributed request."}},"notes":["Send a Bearer token from `thinkroom login` to list account documents. Anonymous requests can still create documents with POST /api/docs."]}