update_doc
Replace a workspace's doc body. Takes EITHER TipTap JSON (content) OR Markdown (markdown): pass markdown when you're producing prose from scratch (CommonMark + GFM is the format every LLM emits natively), pass TipTap JSON when you need structural edits to an existing doc (round-trip from get_doc, mutate, write back). Beyond CommonMark + GFM, the markdown layer recognizes:
→ inline image. Use ANY publicly-reachable URL (HTTPS preferred — HTTP fires browser mixed-content warnings; data: URIs are rejected by
allowBase64: false). Renders block-feeling via CSS (max-width 100%, rounded corners, drop shadow) even though the underlying node is inline. Thealttext is the accessible label and shows in place of the image if the URL fails to load — always include it. To attach a user-uploaded file, hitPOST /api/workspaces/:slug/upload-imagefrom the human-side UI first to get a Vercel Blob URL, then reference that URL in the doc markdown.A lone video-file URL on its own line (extension
.mp4/.m4v/.webm/.mov/.mkv, signed-params + timestamp fragments tolerated) → native HTML5<video controls preload="metadata">player. Source URL is referenced directly: no iframe, no transcoding, no quality loss. Vercel Blob is the canonical hosting (5 GB per file, served with HTTP range requests so 4K masters stream cleanly), but ANY publicly-reachable HTTPS URL works. Sample shape: a paragraph containing onlyhttps://cdn.dock.ai/2025-launch-walkthrough.mp4. Mid-paragraph URLs stay as plain links — surrounding prose disqualifies the auto-promotion (matches the oEmbed convention).```mermaid fenced code → diagram (15 sub-types: flowchart, sequence, gantt, ER, state, class, mindmap, timeline, pie, quadrant, sankey, XY-chart, packet, block, journey)
$x$ inline math, $$x$$ block math (LaTeX, KaTeX-rendered, scripts/href disabled)
> [!NOTE] / [!TIP] / [!IMPORTANT] / [!WARNING] / [!CAUTION] GFM-style callouts
```svg fenced code → sanitized SVG embed (the universal escape hatch for custom diagrams; scripts and event handlers stripped at write time)
XBODY → collapsible toggle
[[slug]] / [[org/slug]] / [[slug#tab]] / [[slug#row-id]] / [[slug|display]] → cross-references to another workspace, surface, or row. Resolved against your accessible workspace set; targets you can't see render as plain text on the reader's side (no info leak). Every cross-ref creates a Backlink row so the target's 'referenced from' sidebar shows this doc.
@Label → @-mention of a user or agent.
<kind>isagentorhuman;<id>is the principal id. Optional query params?org=<slug>(agents) or?email=<addr>(humans) for renderer hints. Mentioning a human writes adoc_mentionrow to their inbox + sends a deep-link email; mentioning an agent fires thedoc.mention_addedwebhook so the agent service can wake up and reply. Re-saving a doc that already mentions someone does NOT re-fire — only newly-added mentions notify (computed from a diff against the previous body). Use this from agent code to ping a teammate when a doc you wrote needs their eyes.A lone URL on its own line from a safelisted provider (YouTube, Vimeo, Loom, Figma, CodePen, GitHub gists) → sandboxed iframe embed. Other URLs stay as regular links. Surrounding prose disqualifies the auto-embed.
Per-format caps: max 50 Mermaid diagrams (30 KB source each), max 500 math expressions (8 KB source each), max 50 SVG blocks (100 KB source each post-sanitize), max 200 cross-refs per doc, max 500 @-mentions per doc, max 20 embeds per doc, max 20 videos per doc (5 GB per file at upload time), max 200 images per doc. See /docs/doc-formats for examples. Last-write-wins; no CRDT merge. Emits doc.updated + doc.heading_added + doc.mention_added events as applicable. Requires editor role. Multi-surface workspaces optionally accept surface_slug to write to a specific doc tab; omitted writes the primary doc surface. Append-only updates have a dedicated append_doc_section tool that doesn't require fetching the body first.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| slug | Yes | The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace. | |
| content | No | TipTap document JSON: `{ type: 'doc', content: [ ... ] }`. Use this when round-tripping from get_doc to preserve formatting. Mutually exclusive with `markdown` (content wins if both are passed). | |
| markdown | No | Markdown body (CommonMark + GFM). Converted server-side to TipTap JSON via the same converter that powers PUT /api/workspaces/:slug/doc. Use this when authoring prose from scratch; no need to hand-build ProseMirror nodes. | |
| surface_slug | No | Optional doc surface slug for multi-doc workspaces. Omit to write the primary doc surface. Use list_surfaces to see available slugs. | |
| if_unmodified_since | No | Optional precondition. ISO 8601 timestamp (typically the `updatedAt` you read via `get_doc`). When set and the doc has changed since this cutoff, the write is rejected with `code: -32602`, message describing the conflict, and `data: { conflict: true, currentUpdatedAt, precondition }` so your agent can refetch + merge instead of silently clobbering a concurrent write. Without this, two agents PUTting near-simultaneously will both succeed and the last write wins (the previous content vanishes). Use this in multi-agent co-authoring flows; skip it for greenfield writes where you know you're the only writer. |