> ## Documentation Index
> Fetch the complete documentation index at: https://docs.superdocs.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Document Sessions

> Work with several open documents in one session — target any of them by id, let the AI move content between them, or have it open a brand-new document.

# 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_id` and 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:

| `open_mode`         | Behavior                                                                           |
| ------------------- | ---------------------------------------------------------------------------------- |
| `replace` (default) | The uploaded file replaces the focused document — the pre-multi-document behavior. |
| `new_focused`       | Opens the file as a **new** document and focuses it. Existing documents stay open. |
| `background`        | Opens the file as a new document but keeps the current focus.                      |

(`new_tab` is also accepted as an alias of `new_focused`.)

<Note>
  **`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.
</Note>

The upload response includes the session's full document roster (document ids, a ready `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

```bash theme={null}
# Roster: every open document + which one is focused.
# Metadata only by default (ids, title, section counts, focused flag) — token-light;
# add ?include_html=true to also get each document's HTML body.
curl https://api.superdocs.app/v1/sessions/my-session/documents \
  -H "Authorization: Bearer sk_YOUR_API_KEY"

# Switch focus
curl -X POST https://api.superdocs.app/v1/sessions/my-session/documents/{document_id}/focus \
  -H "Authorization: Bearer sk_YOUR_API_KEY"

# Close one document (the session stays alive); optionally pick the next focus
curl -X DELETE "https://api.superdocs.app/v1/sessions/my-session/documents/{document_id}?next_focus={other_id}" \
  -H "Authorization: Bearer sk_YOUR_API_KEY"
```

The same three operations are MCP tools: `list_session_documents`, `focus_session_document`, `close_session_document`.

## Targeting a document in chat

Three ways, in order of precedence:

1. **Explicit `document_id`** on the chat request (REST `/v1/chat`, `/v1/chat/async`, or the MCP `chat`/`chat_async` tools). That document also becomes the focus for the turn.
2. **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".
3. **Neither** — the turn targets the focused document.

```bash theme={null}
curl -X POST https://api.superdocs.app/v1/chat \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Standardize the date format in this document",
    "session_id": "my-session",
    "document_id": "doc_2f6c…"
  }'
```

The AI can also work **across** documents in one turn: read one document and apply what it found to another ("use the rates from the rate card to fill the invoice"), search all open documents at once, or update several documents in a single request.

## 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 in `changes[]` carries its `document_id`, so you can group the approval list per document. See [SSE Streaming](/guides/streaming#proposed_change_batch).
* `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](/guides/streaming#documents_changed).
* The `final` result includes `focused_document_id`.
* Document payloads (upload responses, the roster, session history) may include a nullable `page_setup` object — 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](/concepts/sessions#revert-a-session-to-a-previous-message) 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`.

```bash theme={null}
curl -X POST https://api.superdocs.app/v1/sessions/my-session/revert \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"turn_index": 4, "dry_run": true}'
```

**Apply per document, not by reloading (`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](/guides/editor-integration#applying-ai-updates-safely-without-losing-user-edits)). 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).

```bash theme={null}
curl -X POST https://api.superdocs.app/v1/sessions/my-session/redo \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"turn_index": 4, "redo_checkpoint_id": "ckpt_…"}'
```

While a chat job on the session is in progress or awaiting approval, both `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_summary` summarizes what actually changed (e.g. "2 sections edited, 1 added, rest untouched"), and `change_history` / compact-mode `chunk_diffs` carry 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-document `revert_changes` map, then commit the revert — and `redo` remains 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's `document_changes` reflects the committed state, and genuine same-section concurrent conflicts are surfaced explicitly — a `concurrent_merges` block plus a notice appended to the reply — never resolved silently. Cross-session changes announce themselves as [document events](/guides/concurrent-editing#polling-for-other-sessions-edits) 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; call `list_session_documents` to 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.
