Skip to content
LogoLogo

Replace versioned Document content

Replace versioned Document content

PUT/api/v1/documents/{document_id}/content

Canonical content with an unsupported schema or an attempted schema 2-to-1 downgrade returns 409 document_schema_version_unsupported. Restoring an older Document revision retains an existing schema 2 floor. Replaces Document content through versioned optimistic autosave. The client sends the revision it edited in If-Match. A stale revision is refused with 409 carrying the current server content, so the client can keep its local draft and offer compare, reload, or retry without a second request. Success means durably acknowledged.

This is not a collaborative editing protocol: no CRDT, operational transform, presence, or admission behaviour is implied.

Path Parameters

document_idRequired
string <uuid>
Example0198ac5e-f9b7-7000-8000-000000000001

Header Parameters

X-CSRF-TokenRequired
string·min 43·max 43

Session-bound CSRF token. It must exactly match the __Host-assign_csrf cookie sent with the request.

If-MatchRequired
string

Entity tag containing the resource version last observed by the client.

Request Body

application/json
contentRequired
object

Versioned, server-validated rich-text document. The server accepts only nodes and marks on the Assign allowlist and rejects anything outside it; the client's installed editor extension set is never authoritative. Complexity is bounded at 32 levels of nesting and 50,000 nodes in addition to the per-field byte limits.

Accepted nodes: doc, paragraph, text, heading, bulletList, orderedList, listItem, taskList, taskItem, blockquote, callout, codeBlock, blockMath, horizontalRule, hardBreak, image, file, plus the reference nodes userMention, projectReference, documentReference, and taskReference. Accepted marks: bold, italic, underline, strike, code, link.

A Task-description or Comment mutation accepts at most 100 distinct canonical user identifiers across its userMention nodes. Repeated mentions of the same user count once.

image and file name an Assign attachment through attrs.attachmentId and carry no URL and no bytes: attachments travel only through the attachment upload and download operations, and a signed URL is minted per request after a fresh authorization check rather than stored in content. A request whose image or file node omits attachmentId, or carries a URL-bearing attribute, is rejected.

callout has block children and exactly attrs.kind and attrs.icon. Kind is callout, note, tip, important, warning, or caution. Icon is null for the default Lucide icon, an emoji of at most 64 UTF-8 bytes, an allowlisted lucide:name token to override it, or an empty string to hide it. Unknown Lucide names are rejected. HTML, URLs and image data are not accepted as icons. Callouts are additive in schemas 1 and 2.

blockMath stores a required string attrs.latex; rendered math HTML and renderer settings are not persisted. Schema 1 retains the existing node contract; schema 2 additionally admits tables. Checklists, attachment blocks, display equations, underline, and projectReference were added additively, so every previously stored document is still valid and no migration is required.

Schema 2 additionally accepts table, tableRow, tableCell and tableHeader. A table has tableRow children; a row has tableCell/tableHeader children. Cells contain Assign blocks, starting with a paragraph; nested tables are rejected. Cells accept colspan/rowspan (integers 1–64, default 1), colwidth (null or one integer 24–2000 per spanned column), and align (null, left, center or right). Tables/rows have no attrs, marks or text; cells have no marks/text or other attributes. The occupancy grid must be rectangular with no overlap, holes or spans beyond its rows. Limits are 1000 rows, 64 logical columns and 10000 physical cells, still subject to each field's byte budget and global node/depth limits. Schema 1 rejects all four table nodes. Rich tables exchange through the named assign-table Markdown JSON fence; simple tables use GFM pipes.

Example{ "schema_version": 1, "type": "doc", "content": [] }

Responses