Multi-Document Sessions
A SuperDocs session can hold several open documents at once — like tabs in an editor. The AI sees all of them, can read or search any of them, and edits whichever one your request targets. One document is always the focused document: the default target for any chat turn that doesn’t say otherwise. Everything here works identically over the REST API and the MCP tools.The mental model
- Session — one conversation thread with the AI.
- Open documents — the set of documents loaded in that session, each with a stable
document_idand a title. - Focused document — the one a turn targets when you don’t specify a
document_id. Uploading with default settings replaces it; single-document integrations never need to think about any of this (one document = it’s always focused).
Opening more documents
POST /v1/documents/upload accepts an open_mode form field:
(
new_tab is also accepted as an alias of new_focused.)
open_mode is on the multipart upload only. POST /v1/documents/upload (multipart) is where you choose new_focused / background. The agent-facing upload tools — upload_document_base64 and the pre-signed process_uploaded_document — don’t take open_mode; they load the file as the focused document (replacing the current one). For an agent to open an additional document as a new tab, open an already-saved document with open_documents / init_session, or just ask the AI in chat to put the content in a new document. So this one upload detail is not identical across REST-multipart and the MCP upload tools.title for each, section counts, and which one is focused), so you can render tabs immediately — use the title field for the tab label. The roster is token-light by default: it returns metadata only and the HTML body is omitted (null). Pass include_html=true when you actually need the body.
The AI can also create a new document itself: ask in chat — “put the summary in a new document called Q3 Summary” — and the turn opens a fresh document with the generated content (this bills one operation like other content creation).
Listing, focusing, closing
list_session_documents, focus_session_document, close_session_document.
Targeting a document in chat
Three ways, in order of precedence:- Explicit
document_idon the chat request (REST/v1/chat,/v1/chat/async, or the MCPchat/chat_asynctools). That document also becomes the focus for the turn. - Name it in the message — “fix the totals in the invoice” routes to the open document titled like an invoice. The AI resolves references the way a colleague would, including follow-ups like “now add those notes to the meeting doc”.
- Neither — the turn targets the focused document.
What changes on the wire
If you only ever open one document per session, nothing changes. With multiple documents:proposed_change_batch(review mode): every change inchanges[]carries itsdocument_id, so you can group the approval list per document. See SSE Streaming.documents_changed: emitted when an auto-apply turn changed 2+ documents, or when a document was created (in either approval mode) — so background tabs can badge and new tabs appear. See SSE Streaming.- The
finalresult includesfocused_document_id. - Document payloads (upload responses, the roster, session history) may include a nullable
page_setupobject — the page size, orientation, and margins detected from.docx/PDF uploads — useful if your UI renders true-size pages.
Reverting without a whole-document swap
POST /v1/sessions/{id}/revert rewinds the conversation and the document to the state just before a chosen user message (see Revert a session for the base flow). For a multi-document integration, three extra fields let you apply a revert as precisely as you apply a normal AI turn — and undo it if needed.
Preview before you commit (dry_run). Send "dry_run": true to compute what the revert would change without committing or archiving anything. The response comes back with dry_run: true and a populated revert_changes you can show the user (“this will roll back 3 sections in the invoice”) before they confirm. Nothing is touched until you call it again without dry_run.
revert_changes). The revert response carries revert_changes — a map of document id → the changes for that document. Apply each document’s changes the same way you apply a normal AI result: per section, onto the live editor (see Applying AI updates safely). This matters in a multi-document session: a single revert can touch several open documents, and a user may be mid-edit in one of them. Applying revert_changes section-by-section preserves their in-progress typing and lets the user keep their version and accept the other as a suggestion on a clashed section, exactly like a live edit. Whole-document setContent per tab would throw that away. document_state + editor_action are still returned for the focused document as a fallback for simple single-document UIs.
Undo a revert (redo). Revert is non-destructive. The response includes redo_checkpoint_id — the pre-revert head. Pass it (with the same turn_index) to POST /v1/sessions/{id}/redo to fork forward to the pre-revert state and un-archive exactly the turns the revert hid. redo returns the same shape as revert (including its own revert_changes to re-apply per section). It’s meant for use immediately after a revert — once a new chat message is sent the branch diverges, so stop offering redo at that point. redo_checkpoint_id is null on a dry-run and on a first-message reset (there’s nothing to fork forward to).
revert and redo return 409 — settle or cancel the job first.
When an edit fails or goes wrong
Multi-document turns are designed so you can always tell what state each document is in — nothing is left ambiguous after a failure:- Failed turns say so, and bill 0. If a requested edit could not be applied, the response text reports the real outcome instead of claiming success, and no operation is charged. A turn that partially applied reports per-section results:
document_changes.changes_summarysummarizes what actually changed (e.g. “2 sections edited, 1 added, rest untouched”), andchange_history/ compact-modechunk_diffscarry the per-section before/after for audit. - Bad output is reversible per document. If the AI writes a malformed structure (say, into a nested table), you don’t have to reload anything: preview the rollback with
POST /v1/sessions/{id}/revert+"dry_run": true, inspect the per-documentrevert_changesmap, then commit the revert — andredoremains available immediately afterward if the user changes their mind. See “Reverting without a whole-document swap” above. - Versions are the ground truth. Every stored document carries a monotonically increasing
version; each committed change (AI turn, autosave, revert, another session’s edit) bumps it. A response’sdocument_changesreflects the committed state, and genuine same-section concurrent conflicts are surfaced explicitly — aconcurrent_mergesblock plus a notice appended to the reply — never resolved silently. Cross-session changes announce themselves as document events you can poll.
Notes
- Document order in the roster is stable insertion order — safe to use as tab order.
- Session history restore (
get_session_history) returns the conversation plus the focused document’s state; calllist_session_documentsto rebuild the full tab set when restoring a multi-document session. - Closing a document doesn’t delete anything from your storage; it just removes it from the session.

