Skip to content

Docs

Knobs Docs are collaborative rich-text documents. REST covers metadata, sharing, content reads, trash, image assets, and import/export. Live editing does not flow through REST — the apps push CRDT frames over the realtime connection after joining via the open call; a REST client reads materialized content instead.

All routes are workspace-scoped and accept a session JWT or API key. Docs are private by default with per-doc ACLs: a doc you can’t view reads as 404.

Endpoint Description
GET /v1/workspaces/{wid}/docs List docs you can read (?limit=)
POST /v1/workspaces/{wid}/docs Create {title} → 201 — private to you by default
GET /v1/workspaces/{wid}/docs/{did} Doc metadata + latest snapshot ref (view access)
PATCH /v1/workspaces/{wid}/docs/{did} Exactly one of {title} (rename, edit access), {trash: true}, or {restore: true} (owner/admin)
DELETE /v1/workspaces/{wid}/docs/{did} Permanently purge a trashed doc → 202 {purging: true} (async)
POST /v1/workspaces/{wid}/docs/{did}/open Join for live editing: verifies access, returns metadata + a signed snapshot URL. Used by collaborative clients; plain REST readers don’t need it
GET /v1/workspaces/{wid}/docs/trash List trashed docs
POST /v1/workspaces/{wid}/docs/trash:empty Purge everything in the trash → 202 {purging: n}
Endpoint Description
GET /v1/workspaces/{wid}/docs/{did}/content Materialized document bytes; ?format=html (default) or ?format=md. Serves a strong ETag and honors If-None-Match
Terminal window
curl -H "Authorization: Bearer knak_..." \
"https://api.knobs.io/v1/workspaces/{wid}/docs/{did}/content?format=md"

The response body is the document itself (text/html or text/markdown), not JSON. Heavier formats (DOCX, PDF, …) go through the async export flow below.

The access model is ownerUid + workspaceAccess (none/view/comment/edit, what any workspace member gets) + grants (per-principal levels; principals are user uids or group ids grp_…). Workspace admins/owners implicitly have owner-level access.

Endpoint Description
GET /v1/workspaces/{wid}/docs/{did}/access The sharing panel: {ownerUid, workspaceAccess, grants} (owner or workspace admin)
PUT /v1/workspaces/{wid}/docs/{did}/access Wholesale replace {workspaceAccess, grants} — the owner’s grant is server-maintained and must not appear
Terminal window
curl -X PUT https://api.knobs.io/v1/workspaces/{wid}/docs/{did}/access \
-H "Authorization: Bearer knak_..." \
-H "Content-Type: application/json" \
-d '{"workspaceAccess": "view", "grants": {"u_5ac2…": "edit", "grp_design…": "comment"}}'

Anchored comment threads live on the doc. A thread carries a plain-text body, optional mentions (member uids), optional selection anchors (anchorFrom/anchorTo, opaque editor-generated strings) with a quotedText fallback, embedded replies, and a resolved flag. Listing requires view access; adding, replying, and resolving require comment access; editing a body is author-only; deleting is author or owner/admin.

Endpoint Description
GET /v1/workspaces/{wid}/docs/{did}/comments List threads, oldest first → {comments}
POST /v1/workspaces/{wid}/docs/{did}/comments Start a thread {body, mentions?, anchorFrom?, anchorTo?, quotedText?, suggestionId?} → the thread
POST /v1/workspaces/{wid}/docs/{did}/comments/{cid}/replies Reply {body, mentions?} → the updated thread (≤100 replies)
POST /v1/workspaces/{wid}/docs/{did}/comments/{cid}/resolve `{resolved: true
PATCH /v1/workspaces/{wid}/docs/{did}/comments/{cid} Edit the body {body} (author only)
DELETE /v1/workspaces/{wid}/docs/{did}/comments/{cid} Delete the thread → 204

A version is the document’s state pinned under a name. A turn is a version plus exchange metadata — one step of a redline negotiation. Compare synthesizes a redline (tracked changes) between any two versions. Restore is never destructive: it appends an edit that brings the live document back to the pinned state, so history — including the restore itself — is preserved.

Endpoint Description
GET /v1/workspaces/{wid}/docs/{did}/versions Pinned versions, newest first → {versions}
POST /v1/workspaces/{wid}/docs/{did}/versions Pin the current state {name} → the version (edit access)
POST /v1/workspaces/{wid}/docs/{did}/versions/{vid}/restore Restore the live doc to a pinned version (edit access)
GET /v1/workspaces/{wid}/docs/{did}/versions/compare ?from={vid}&to={vid or live} — the redline between two versions → {redline} (ProseMirror JSON); add format=docx to download it as a tracked-changes Word file
GET /v1/workspaces/{wid}/docs/{did}/turns The exchange timeline, newest first → {turns}
POST /v1/workspaces/{wid}/docs/{did}/turns Pin the current state as a turn {name, direction: sent|received|internal, counterparty?} (edit access)
POST /v1/workspaces/{wid}/docs/{did}/turns/export-pair “Send to counsel”: {name, counterparty?, scrub?} → 202 with a sent turn plus two export jobs (a clean and a tracked-changes Word file) whose ids also land on the turn (edit access)
POST /v1/workspaces/{wid}/docs/{did}/turns/import Import a returned Word file as a turn: multipart file (.docx) + name + optional counterparty → 202 {job}. Tracked changes land as suggestions (attributed to the counterparty when the file is clean or the doc moved on); its comments become threads; the pre-import state is pinned for one-click undo (edit access)
GET /v1/workspaces/{wid}/docs/{did}/turns/imports/{jid} Turn-import job status → {job} with turnId, preImportVersionId, suggestionCount, and warnings when done
POST /v1/workspaces/{wid}/docs/{did}/suggestions/accept-all Apply every pending suggestion (edit access)
POST /v1/workspaces/{wid}/docs/{did}/suggestions/reject-all Undo every pending suggestion (edit access)

External reviewers open one document from a private magic link — no Knobs account. Guests are always commenters: everything they type lands as suggestions, and their Word-style comments appear under their email address. Links expire after 30 days; revoking one cuts access immediately, including live editing sessions.

Endpoint Description
GET /v1/workspaces/{wid}/docs/{did}/guests List a doc’s guest links (owner/admin) → {guests}
POST /v1/workspaces/{wid}/docs/{did}/guests Create a guest link {email} → {guest, link} — the link is returned once; share it yourself (owner/admin)
DELETE /v1/workspaces/{wid}/docs/{did}/guests/{gid} Revoke a guest link
POST /v1/guest/sessions Public: redeem a link’s token {token} → a sign-in credential plus the document’s coordinates. Invalid, expired, and revoked links all answer 404
GET /v1/guest/doc Guest: the document’s metadata (guest credential as bearer)
POST /v1/guest/doc/open Guest: join the live document
GET /v1/guest/doc/comments Guest: list comment threads
POST /v1/guest/doc/comments Guest: start a thread
POST /v1/guest/doc/comments/{cid}/replies Guest: reply to a thread
POST /v1/guest/doc/comments/{cid}/resolve Guest: resolve or reopen a thread
GET /v1/guest/doc/assets/{aid} Guest: resolve an image asset → {url}

Send a document to Google Docs as a native Google Doc — tracked changes become Google suggestions — let the other side edit there, then pull their edits back as a review turn. Requires connecting your Google account (per user, drive.file scope: Knobs only touches files it creates). If Google integration isn’t configured on the deployment, the status route says so — {configured:false} with a normal 200 — and the rest answer 503.

Endpoint Description
GET /v1/users/me/connections/google/start Begin connecting → {url} — open it to grant access
GET /v1/users/me/connections/google Connection status → {connected, email, configured}. configured:false means this deployment has no Google integration — a normal 200, so clients can hide the feature without treating it as an error
DELETE /v1/users/me/connections/google Disconnect (Knobs forgets the credential)
POST /v1/workspaces/{wid}/docs/{did}/google/link Bind the doc to a Google Doc {fileId} (edit access). Pushing without a link creates the file and links it automatically
DELETE /v1/workspaces/{wid}/docs/{did}/google/link Unbind
POST /v1/workspaces/{wid}/docs/{did}/google/push Send the doc {variant?: "redline"|"clean", scrub?} → {fileId, url}; records a sent turn. Redline (default) carries suggestions into Google
POST /v1/workspaces/{wid}/docs/{did}/google/pull Import the Google Doc’s current state {name?} → 202 {job} — poll the turn-import job; edits land as suggestions on a received turn

AI works only through suggestions — it never edits the document directly. If AI isn’t configured on the deployment these routes answer 503.

Endpoint Description
POST /v1/workspaces/{wid}/docs/{did}/turns/{tid}/analyze Ask AI to summarize and risk-rate a turn’s changes → 202; the result appears on the turn’s analysis (summary, per-change explanations with low/medium/high risk, flags)
POST /v1/workspaces/{wid}/docs/{did}/ai/propose Ask AI to revise the document {instruction} → 202 {job}. The proposed edits land as ordinary suggestions authored ai:<your uid> — accept or reject them in the review panel
GET /v1/workspaces/{wid}/docs/{did}/ai/jobs/{jid} AI revision job status → {job} with suggestionCount when done

AI revision currently supports text documents (no images) without pending suggestions, up to ~120 KB of content.

Editor images upload directly to storage via a signed URL; the doc references the stable asset id.

Endpoint Description
POST /v1/workspaces/{wid}/docs/{did}/assets Start an upload {mime, size} → {assetId, url, headers} — PUT the bytes to url with headers (edit access)
GET /v1/workspaces/{wid}/docs/{did}/assets/{aid} Resolve an asset → {url} (short-lived signed download URL; view access)

Both directions are async jobs. Supported formats include TXT, Markdown, HTML, DOCX, ODT, RTF, and (export-only) PDF.

Endpoint Description
POST /v1/workspaces/{wid}/docs/import Multipart upload (file + optional title) → 202 {job} — the new doc is private to you
GET /v1/workspaces/{wid}/doc-imports/{jid} Import job status → {job} (includes the doc id when done)
POST /v1/workspaces/{wid}/docs/{did}/export Start an export {format, variant?, scrub?} → 202 {job}. variant: "redline" (DOCX only) keeps pending suggestions as Word tracked changes and includes the doc’s comments; scrub: true anonymizes authors and drops dates while keeping the markup. The default ("clean") exports with all suggestions accepted
GET /v1/workspaces/{wid}/docs/{did}/exports/{jid} Export job status → {job, url} — url is a fresh signed download link once ready
Terminal window
curl -X POST https://api.knobs.io/v1/workspaces/{wid}/docs/import \
-H "Authorization: Bearer knak_..." \
-F "file=@spec.docx" -F "title=Product spec"

Import files are capped at 20 MiB (413 too_large); unsupported types return 400 unsupported_format.

  • Sheets — the same sharing model on workbooks.
  • Tasks — tasks:create-from-doc source-links a task to a doc.