> ## 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.

# Documents

> Load documents from files, edit them with AI, create new ones from scratch, and export as Word files.

# Documents

## Sending document HTML

Send your document HTML in `document_html`. The AI reads it, makes changes, and returns updated HTML in `document_changes.updated_html`.

**The server keeps your document between turns.** Once a session holds a document, you don't re-send it every turn. Send just the `message` and the AI works on the copy it already has. Include `document_html` only to **load** a document into a session the first time, **replace** it wholesale, or **sync** edits you made to the HTML outside of chat (covered below).

**The one rule, for when you *do* send HTML back: send it faithfully.** If the user edited the document in your own editor and you're syncing those edits, send the full current HTML exactly as your editor has it. Don't programmatically strip, modify, or reformat it. If your editor or HTML sanitizer removes custom `data-*` attributes, configure it to preserve them. Those `data-chunk-id` attributes are what let the AI make targeted edits.

### Example flow

```python theme={null}
import requests

API_KEY = "sk_YOUR_API_KEY"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

# 1. Send a document
response = requests.post("https://api.superdocs.app/v1/chat", headers=HEADERS, json={
    "message": "Add a summary at the top",
    "session_id": "my-doc",
    "document_html": "<h1>Report</h1><p>Q4 revenue increased by 15%.</p>"
})

data = response.json()
updated_html = data["document_changes"]["updated_html"]

# 2. Follow-up turn: the server still holds the document, so send just the message
response = requests.post("https://api.superdocs.app/v1/chat", headers=HEADERS, json={
    "message": "Make the summary more concise",
    "session_id": "my-doc",
})

# Re-send document_html ONLY when you changed the HTML outside chat
# (e.g. the user edited it in your editor) and want the AI to see those edits:
#
# response = requests.post("https://api.superdocs.app/v1/chat", headers=HEADERS, json={
#     "message": "Make the summary more concise",
#     "session_id": "my-doc",
#     "document_html": edited_html,  # send it exactly as your editor has it
# })
```

The returned HTML contains `data-chunk-id` attributes on elements. These identify document sections and enable the AI to make targeted edits without reprocessing the entire document. These same IDs appear in API responses — for example, `chunk_id` and `insert_after_chunk_id` in [HITL proposed changes](/guides/human-in-the-loop#understanding-proposed-changes).

If you make an edit you want to undo, you can rewind both the chat history and the document state to before any user message — see [Revert a session to a previous message](/concepts/sessions#revert-a-session-to-a-previous-message).

## Loading documents from files

Upload a file to load it as the active document in a session. The file is processed synchronously — the response includes the full document HTML ready for editing.

**Supported formats:** DOCX, DOC, ODT, PDF, TXT, HTML, MD (Markdown), RTF

```bash theme={null}
curl -X POST https://api.superdocs.app/v1/documents/upload \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -F "file=@contract.docx" \
  -F "session_id=my-session"
```

Response:

```json theme={null}
{
  "html": "<div data-chunk-id=\"abc123\"><h1>Contract</h1><p>...</p></div>",
  "session_id": "my-session",
  "filename": "contract.docx",
  "chunks_count": 12,
  "version_id": "v_xyz789"
}
```

**Multiple documents per session.** By default an upload **replaces** the session's focused document. Pass `open_mode=new_focused` to open the file as an additional document (and focus it), or `open_mode=background` to open it without stealing focus. The response also includes the session's document roster (every open document's id + title + which is focused) so you can render tabs immediately. See the [Multi-Document Sessions guide](/guides/multi-document).

**Page geometry.** For DOCX and PDF uploads, document payloads include a nullable `page_setup` object — detected page dimensions (`width_in` / `height_in`), per-side margins (`margin_in`), and `orientation` — anywhere a document is returned (upload responses, the session-documents roster, session history). It's `null` when the source format carries no geometry (e.g. plain text or HTML).

```json theme={null}
{
  "page_setup": {
    "width_in": 8.27,
    "height_in": 11.69,
    "margin_in": {"top": 1.0, "right": 1.0, "bottom": 1.0, "left": 1.0},
    "orientation": "portrait",
    "source": "docx"
  }
}
```

After uploading, you can immediately send chat messages to edit the document:

```python theme={null}
# Upload a document
upload = requests.post("https://api.superdocs.app/v1/documents/upload",
    headers={"Authorization": f"Bearer {API_KEY}"},
    files={"file": open("contract.docx", "rb")},
    data={"session_id": "my-session"}
)
doc_html = upload.json()["html"]

# Edit it with AI
response = requests.post("https://api.superdocs.app/v1/chat", headers=HEADERS, json={
    "message": "Simplify the language in section 3",
    "session_id": "my-session",
    "document_html": doc_html
})
```

## Creating documents from scratch

Send a message to an empty session (no `document_html`, no uploaded file) and the AI will generate a complete document for you.

```python theme={null}
response = requests.post("https://api.superdocs.app/v1/chat", headers=HEADERS, json={
    "message": "Create a non-disclosure agreement between two companies",
    "session_id": "new-nda"
})

data = response.json()
new_document_html = data["document_changes"]["updated_html"]
```

Example prompts that work well:

* "Create a consulting agreement"
* "Draft a project proposal for a mobile app"
* "Write a company privacy policy"
* "Create a meeting minutes template"

The AI generates structured, formatted HTML that you can then continue editing with follow-up messages.

## Exporting documents

Export the current document as a downloadable file.

**Three ways to supply the document:**

* **Inline HTML** — send HTML content in `html` (use when you have the document HTML in your app, up to 20 MB)
* **Export from a session** — send a `session_id` (the API retrieves the document from the session)
* **Pre-signed upload then export** — upload the HTML to a pre-signed URL first, then send `upload_id` (use for documents between 20 MB and 100 MB — see [Large documents](#large-documents) below)

**Five output formats:**

* `docx` (default) — Microsoft Word (Open XML), preserves tables, formatting, embedded images
* `pdf` — paginated, print-ready PDF
* `html` — standalone HTML file with inlined CSS
* `markdown` — Markdown (.md) with ATX headings
* `txt` — plain text

A sixth value, `doc` (Word-compatible HTML wrapper), is accepted as a legacy alias and will be removed in a future release. New integrations should use `docx`.

### Export from inline HTML

```bash theme={null}
curl -X POST https://api.superdocs.app/v1/documents/export \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>My Document</h1><p>Content here...</p>",
    "format": "docx",
    "options": { "filename": "my-document" }
  }' \
  --output my-document.docx
```

### Export from a session

```bash theme={null}
curl -X POST https://api.superdocs.app/v1/documents/export \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "my-session",
    "format": "pdf"
  }' \
  --output exported-document.pdf
```

### Export in Python

```python theme={null}
response = requests.post("https://api.superdocs.app/v1/documents/export",
    headers=HEADERS,
    json={
        "session_id": "my-session",
        "format": "docx",
        "options": {
            "paper_size": "A4",
            "margins": "normal",
            "filename": "final-contract"
        }
    }
)

with open("final-contract.docx", "wb") as f:
    f.write(response.content)
```

### Request parameters

| Parameter    | Type   | Required                                            | Description                                                                                          |
| ------------ | ------ | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `html`       | string | One of `html` / `session_id` / `upload_id` required | HTML content to convert (inline HTML)                                                                |
| `session_id` | string | One of `html` / `session_id` / `upload_id` required | Session to export from                                                                               |
| `upload_id`  | string | One of `html` / `session_id` / `upload_id` required | Pre-signed upload reference (for large documents)                                                    |
| `format`     | string | No                                                  | `"docx"` (default), `"pdf"`, `"html"`, `"markdown"`, or `"txt"`. `"doc"` accepted as a legacy alias. |
| `options`    | object | No                                                  | Customisation block — see [Export options](#export-options) below.                                   |
| `filename`   | string | No                                                  | Legacy top-level filename. Prefer `options.filename`, which wins when both are set.                  |

### Export options

Pass an `options` object to customise the rendered output. Defaults are sensible for English-language documents; override only what you need.

| Field                   | Type    | Default      | Description                                                                                                                                                                                                                                                                                                                                      |
| ----------------------- | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `paper_size`            | string  | `"Letter"`   | `"Letter"`, `"A4"`, `"A3"`, or `"Legal"`. Applies to DOCX/PDF.                                                                                                                                                                                                                                                                                   |
| `orientation`           | string  | `"portrait"` | `"portrait"` or `"landscape"`. Applies to DOCX/PDF.                                                                                                                                                                                                                                                                                              |
| `margins`               | string  | `"normal"`   | `"narrow"` (0.5 in), `"normal"` (1 in), `"wide"` (1.5 in), or `"custom"`.                                                                                                                                                                                                                                                                        |
| `custom_margins_inches` | object  | —            | Required when `margins="custom"`. Object with `top`, `right`, `bottom`, `left` floats between 0.25 and 3.0.                                                                                                                                                                                                                                      |
| `filename`              | string  | —            | Name for the downloaded file. Precedence: `options.filename`, then the legacy top-level `filename`, then a name derived from the document's first heading. Pass it with or without an extension: a supplied extension matching the export format is kept as-is (never doubled), and an extension-less name gets the format's extension appended. |
| `embed_images`          | boolean | `false`      | HTML export only. When `true`, images are base64-embedded for offline portability. Raises the size cap from 100 MB to 150 MB.                                                                                                                                                                                                                    |
| `watermark_text`        | string  | —            | PDF only. Optional text watermark overlaid on every page (max 64 chars).                                                                                                                                                                                                                                                                         |
| `watermark_opacity`     | number  | `0.3`        | PDF watermark opacity, between 0.05 and 1.0.                                                                                                                                                                                                                                                                                                     |

```python theme={null}
options = {
    "paper_size": "A4",
    "orientation": "landscape",
    "margins": "wide",
    "filename": "Q4-Report",
    "watermark_text": "DRAFT",
    "watermark_opacity": 0.15,
}
```

The response is a binary file download with the appropriate `Content-Type` header. The `Content-Disposition` header carries the filename (RFC 5987 encoded for non-ASCII characters).

### Non-fatal warnings

Exports may complete successfully but with non-fatal issues — an image URL that 404'd, a diagram that exceeded the render timeout, an unsupported field code that was skipped. The response carries these in the `X-Export-Warnings` header as a base64-encoded JSON list. The header is always present alongside `Content-Disposition` in `Access-Control-Expose-Headers`, so browser clients can read it via `fetch`.

```javascript theme={null}
const response = await fetch('/v1/documents/export', { ... });
const warningsHeader = response.headers.get('X-Export-Warnings');
if (warningsHeader) {
  const warnings = JSON.parse(atob(warningsHeader));
  // [{ code: "image_download_failed", message: "...", detail: { src: "..." } }, ...]
}
```

| Warning code                | Meaning                                                                                                                                                   |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image_download_failed`     | An embedded image URL could not be fetched. The image is omitted from the output.                                                                         |
| `mermaid_render_failed`     | A Mermaid diagram could not be rendered. The diagram source is kept in the output as a code block.                                                        |
| `mermaid_timeout`           | A Mermaid diagram took too long to render. Same fallback as above.                                                                                        |
| `excalidraw_corrupt`        | An Excalidraw drawing's JSON payload could not be parsed.                                                                                                 |
| `footnote_orphan`           | A footnote reference has no matching footnote body.                                                                                                       |
| `track_change_unmatched`    | A tracked change anchor could not be placed inline; the change is appended at the end of the document.                                                    |
| `field_code_unsupported`    | A citation or other field code could not be re-emitted in the output format.                                                                              |
| `watermark_skipped`         | A requested PDF watermark could not be applied.                                                                                                           |
| `embed_image_skipped`       | An image could not be inlined into an HTML export with `embed_images=true`.                                                                               |
| `size_cap_warning`          | The document is close to the per-format size limit.                                                                                                       |
| `header_footer_degraded`    | A rich page header or footer could not be fully reproduced; it was emitted as best-effort text with working page-number fields.                           |
| `sections_degraded`         | Per-section page geometry (a section with its own page size or orientation) could not be applied in this PDF render; the export uses a single page setup. |
| `svg_png_conversion_failed` | An SVG image could not be converted for `.docx` output; the original SVG is kept in the file.                                                             |
| `math_partial_fidelity`     | An equation contained a construct that could not be fully converted to native Word math; that construct is kept as its LaTeX source inside the equation.  |

### Large documents

There's one size story for export, driven by how big the document HTML is:

**Three-tier flow:**

| Document HTML size | Path                                                                                                                                                      |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Up to 20 MB        | Direct POST to `/v1/documents/export` with inline `html` or a `session_id`.                                                                               |
| 20 MB – 100 MB     | Request a pre-signed upload URL via `POST /v1/uploads` (`purpose: "export-html"`), PUT the HTML to it, then call `/v1/documents/export` with `upload_id`. |
| Over 100 MB        | Use the [email-fallback endpoint](#email-fallback-for-very-large-documents).                                                                              |

Keep inline POSTs at or under 20 MB. Above that, the hosted transport layer rejects the request (a hard \~32 MB clamp sits in front of the renderer), so switch to the upload-then-export pattern (PUT the HTML to a pre-signed URL, then export by `upload_id`) rather than pushing a bigger body through directly.

```python theme={null}
# 1. Request a pre-signed PUT URL
upload = requests.post("https://api.superdocs.app/v1/uploads",
    headers=HEADERS,
    json={
        "filename": "export.html",
        "content_type": "text/html",
        "size_bytes": len(html.encode()),
        "purpose": "export-html",
    }
).json()

# 2. PUT the HTML directly to storage
requests.put(upload["upload_url"],
    headers={"Content-Type": "text/html"},
    data=html)

# 3. Export, referencing the upload_id
response = requests.post("https://api.superdocs.app/v1/documents/export",
    headers=HEADERS,
    json={
        "upload_id": upload["upload_id"],
        "filename": "export.html",
        "format": "docx",
    }
)

with open("output.docx", "wb") as f:
    f.write(response.content)
```

The signed URL is valid for 5 minutes; the uploaded blob is retained for 24 hours.

### Email fallback for very large documents

For documents over 100 MB, render asynchronously and deliver via email. The endpoint accepts a session ID, queues a background job, and emails a 7-day signed download link to the recipient.

```bash theme={null}
curl -X POST https://api.superdocs.app/v1/documents/export/email-request \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "my-session",
    "format": "docx",
    "recipient_email": "user@example.com",
    "options": { "paper_size": "A4" }
  }'
```

Response:

```json theme={null}
{
  "job_id": "9c2f...e3a",
  "recipient_email": "user@example.com",
  "status": "queued",
  "eta": "24h",
  "message": "Your file will be emailed to user@example.com within 24 hours."
}
```

`recipient_email` falls back to the email on file for the authenticated user account when omitted. The job appears in `/v1/jobs/{job_id}` with `job_type: "large_export"`. See [Async jobs](/guides/async-jobs) for the polling pattern.

### Size and error responses

| Format                          | Cap    |
| ------------------------------- | ------ |
| `docx`, `pdf`, `html`, `doc`    | 100 MB |
| `html` with `embed_images=true` | 150 MB |
| `markdown`, `txt`               | 50 MB  |

Keep inline POSTs at or under 20 MB; the hosted transport layer enforces a hard \~32 MB clamp and returns `413 Request Entity Too Large` before reaching the renderer, so use the pre-signed upload-then-export pattern for anything larger. Requests above the format-specific cap return a structured 413 with a JSON body explaining the limit and pointing to the email-fallback endpoint — see [Error codes](/errors/error-codes#413-request-entity-too-large).

## Supported formatting

The AI can apply the following formatting when creating or editing documents:

* **Text styling** — bold, italic, underline, strikethrough
* **Headings** — H1 through H6
* **Lists** — ordered, unordered, and nested
* **Text highlighting** — with color options (e.g., yellow, green, red)
* **Text color** — change the color of specific text
* **Links** — clickable hyperlinks
* **Tables** — with rows and columns
* **Blockquotes** — indented quotation blocks
* **Code blocks** — for code snippets
* **Horizontal rules** — section dividers

Ask for formatting in plain language: "highlight the key terms in yellow", "make the title bold and larger", "add a table comparing the two options".
