From 9200558ea1041d998b2701f4287d81081fbbe50c Mon Sep 17 00:00:00 2001 From: Oviya Date: Thu, 20 Aug 2026 14:37:07 +0530 Subject: [PATCH 1/2] Add task2: spec-driven client generation pipeline --- .../.gitattributes | 4 + .../.github/workflows/pipeline.yml | 38 + .../doctask2-oviya-senthilkumar/.gitignore | 17 + .../doctask2-oviya-senthilkumar/Makefile | 26 + .../doctask2-oviya-senthilkumar/README.md | 146 + .../mock/README.md | 13 + .../package-lock.json | 907 ++ .../doctask2-oviya-senthilkumar/package.json | 14 + .../reports/breaking-demo/compat.json | 47 + .../reports/breaking-demo/compat.md | 27 + .../reports/compat.json | 186 + .../reports/compat.md | 54 + .../scripts/compat.py | 140 + .../scripts/fetch.sh | 24 + .../scripts/make_breaking_demo.py | 73 + .../scripts/pipeline.sh | 65 + .../specs/superdocs-breaking.json | 8441 ++++++++++++++++ .../specs/superdocs-new.json | 8505 +++++++++++++++++ .../specs/superdocs-old.json | 7364 ++++++++++++++ .../tests/python/test_smoke.py | 28 + .../tests/typescript/smoke.ts | 35 + 21 files changed, 26154 insertions(+) create mode 100644 extensions/doctask2-oviya-senthilkumar/.gitattributes create mode 100644 extensions/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml create mode 100644 extensions/doctask2-oviya-senthilkumar/.gitignore create mode 100644 extensions/doctask2-oviya-senthilkumar/Makefile create mode 100644 extensions/doctask2-oviya-senthilkumar/README.md create mode 100644 extensions/doctask2-oviya-senthilkumar/mock/README.md create mode 100644 extensions/doctask2-oviya-senthilkumar/package-lock.json create mode 100644 extensions/doctask2-oviya-senthilkumar/package.json create mode 100644 extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json create mode 100644 extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md create mode 100644 extensions/doctask2-oviya-senthilkumar/reports/compat.json create mode 100644 extensions/doctask2-oviya-senthilkumar/reports/compat.md create mode 100644 extensions/doctask2-oviya-senthilkumar/scripts/compat.py create mode 100644 extensions/doctask2-oviya-senthilkumar/scripts/fetch.sh create mode 100644 extensions/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py create mode 100644 extensions/doctask2-oviya-senthilkumar/scripts/pipeline.sh create mode 100644 extensions/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json create mode 100644 extensions/doctask2-oviya-senthilkumar/specs/superdocs-new.json create mode 100644 extensions/doctask2-oviya-senthilkumar/specs/superdocs-old.json create mode 100644 extensions/doctask2-oviya-senthilkumar/tests/python/test_smoke.py create mode 100644 extensions/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts diff --git a/extensions/doctask2-oviya-senthilkumar/.gitattributes b/extensions/doctask2-oviya-senthilkumar/.gitattributes new file mode 100644 index 00000000..f7fdae9e --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/.gitattributes @@ -0,0 +1,4 @@ +# Shell scripts must stay LF so they run on the Linux CI runner (CRLF breaks bash). +*.sh text eol=lf +# Everything else: normalize to LF in the repo. +* text=auto eol=lf diff --git a/extensions/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml b/extensions/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml new file mode 100644 index 00000000..20d1dc54 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml @@ -0,0 +1,38 @@ +name: client-pipeline + +# Regenerates the typed clients from the spec, runs the compatibility gate, and +# smoke-tests both clients against a Prism mock — on every push and PR. +on: + push: + pull_request: + workflow_dispatch: + +jobs: + pipeline: + runs-on: ubuntu-latest # Docker, make, python, node all preinstalled + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - uses: actions/setup-node@v4 + with: + node-version: "20" + + # fetch -> generate -> compat (gate) -> mock -> smoke. + # The compat gate exits non-zero if the old->new spec diff is breaking, + # failing the build; it also asserts the gate fires on the breaking demo. + - name: Run pipeline + run: make all + + - name: Upload compatibility report + if: always() + uses: actions/upload-artifact@v4 + with: + name: compat-report + path: | + reports/compat.md + reports/compat.json + reports/breaking-demo/compat.md diff --git a/extensions/doctask2-oviya-senthilkumar/.gitignore b/extensions/doctask2-oviya-senthilkumar/.gitignore new file mode 100644 index 00000000..ecd211e9 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/.gitignore @@ -0,0 +1,17 @@ +# Generated clients — regenerated by `make all`, not committed. +/clients/python/ +/clients/typescript/ + +# Local dependencies +/.venv/ +/node_modules/ +__pycache__/ +*.pyc + +# Intermediate diff output (compat.md / compat.json are kept as deliverables) +/reports/changelog.json +/reports/changelog-breaking.json + +# Secrets — never commit. (This pipeline needs none; mock-only.) +.env +*.key diff --git a/extensions/doctask2-oviya-senthilkumar/Makefile b/extensions/doctask2-oviya-senthilkumar/Makefile new file mode 100644 index 00000000..177b58c7 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/Makefile @@ -0,0 +1,26 @@ +# Spec-driven client generation pipeline for the SuperDocs API. +# `make all` runs the whole thing; individual stages are available too. +# Real work lives in scripts/pipeline.sh so it runs identically on CI and locally. + +.PHONY: all fetch generate compat mock smoke clean + +all: ## Run the full pipeline: fetch -> generate -> compat -> mock -> smoke + bash scripts/pipeline.sh + +fetch: ## Re-fetch the pinned spec versions and rebuild the breaking demo + bash scripts/fetch.sh + +compat: ## Regenerate the compatibility report (old -> new) with CI gate + docker run --rm -v "$(CURDIR):/work" tufin/oasdiff changelog -f json \ + /work/specs/superdocs-old.json /work/specs/superdocs-new.json > reports/changelog.json + python scripts/compat.py --changelog reports/changelog.json \ + --base specs/superdocs-old.json --revision specs/superdocs-new.json --out reports --gate + +mock: ## Start the Prism mock server on :4010 (Ctrl-C to stop) + docker run --rm --name superdocs-mock -p 4010:4010 -v "$(CURDIR):/tmp" \ + stoplight/prism:4 mock -h 0.0.0.0 /tmp/specs/superdocs-new.json + +clean: ## Remove generated clients, reports, and local deps + rm -rf clients/python clients/typescript reports/changelog*.json \ + reports/compat.* reports/breaking-demo .venv node_modules + -docker rm -f superdocs-mock 2>/dev/null diff --git a/extensions/doctask2-oviya-senthilkumar/README.md b/extensions/doctask2-oviya-senthilkumar/README.md new file mode 100644 index 00000000..3caa248c --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/README.md @@ -0,0 +1,146 @@ +# doctask2 — Spec-Driven Client Generation Pipeline + +Consumes SuperDocs' **published OpenAPI spec** and, in one command, produces +typed API clients for **Python** and **TypeScript**, a **compatibility report** +between two spec versions (with a CI gate that fails on breaking changes), and a +**smoke test per language** against a mock server — no API key, no live service. + +--- + +## What this is & who it serves + +When an API is defined by a spec, the clients, the change-review, and the tests +should all be **generated from that spec**, not hand-maintained. This pipeline is +for the team that owns the SuperDocs API and the developers who consume it: + +- **API owners** get an automated **breaking-change gate** — a PR that changes + the spec incompatibly fails CI before it ships. +- **Client developers** get **typed SDKs** (Python + TypeScript) regenerated + from the spec, and proof they actually work (smoke tests against a mock). + +## One-command run + +```bash +make all # fetch → generate → compat (gate) → mock → smoke +``` + +Requirements: **Docker** (runs the generator, differ, and mock — so a clean +machine needs nothing else installed), plus **python3** and **node** to run the +two smoke tests. No API key. On Windows without `make`, run `bash scripts/pipeline.sh`. + +What it does, in order: + +| Stage | Tool | Output | +|---|---|---| +| Fetch | `curl` (pinned commit SHAs) | `specs/superdocs-old.json`, `superdocs-new.json` | +| Generate | **openapi-generator 7.12** (Docker) | `clients/python/`, `clients/typescript/` | +| **Compat report** | **oasdiff** (Docker) + `scripts/compat.py` | `reports/compat.md` + `compat.json`, **CI gate** | +| Mock | **Stoplight Prism** (Docker) | mock server on `:4010`, schema-valid examples | +| Smoke | pytest (Py) + tsx (TS) | one typed call per client through the mock | + +## Architecture — why each tool + +- **openapi-generator** : it's the industry + standard, supports 40+ targets, and turns "add a language" into a one-line + config change. *Buys:* correctness + breadth for free. *Costs:* generated code + is verbose and opinionated, and OpenAPI 3.1 support needs a recent version + (we pin 7.12 and pass `--skip-validate-spec` because the published spec uses a + few 3.1-only constructs). +- **oasdiff** (not a bespoke differ): purpose-built OpenAPI diff with a + **severity model** — it already knows that a removed endpoint or a + newly-required parameter is *breaking* while an added endpoint is *safe*. Our + `scripts/compat.py` wraps its JSON output into a human report and the CI gate. + *Buys:* correct breaking-change classification. *Costs:* one more binary + (we run it via Docker so nothing is installed). +- **Stoplight Prism** (not a stub server): mocks the API straight from the spec, + returning schema-valid example responses — so the smoke tests exercise the + generated clients with **zero** hand-written server code and **no** API key. +- **Docker for all three**: the reproducibility guarantee. A reviewer with only + Docker installed gets identical tool versions. + +## The compatibility report (the centerpiece) + +`scripts/compat.py` consumes oasdiff's JSON changelog and emits `reports/compat.md` ++ `reports/compat.json`, classifying every change as **breaking** (oasdiff level 3 +— removed endpoint, newly-required param, narrowed type) or **safe** (added +endpoint, new optional field). With `--gate` it **exits non-zero on any breaking +change**, so CI fails on an incompatible spec bump. + +**Result on the real SuperDocs history** (`old → new`, see below): + +> **BACKWARD COMPATIBLE** — 0 breaking, 32 safe changes (12 endpoints added). +> Both specs declare version **2.0.0**, yet the contract changed. *The version +> string alone does not signal compatibility — this diff does.* That gap is the +> whole reason this report exists. + +**Proving the gate actually fires:** the pipeline also diffs the new spec against +a synthesized breaking revision (`specs/superdocs-breaking.json`) and asserts the +gate returns non-zero. It flags exactly the three injected breaks: + +``` +request-parameter-became-required POST /v1/chat authorization became required +api-path-removed-without-deprecation POST /v1/documents/export path removed +request-parameter-type-changed GET /v1/documents/{id} document_id string → integer +``` + +## Where the "previous" spec version came from + +The task needs two spec versions to diff. **We used real published history, not a +fabricated one:** + +- **New** = `superdocsapp/docs@ac0d44c` (2026-07-23), 77 paths. +- **Old** = `superdocsapp/docs@a8351de` (2026-06-24), 65 paths. + +Both are the genuine `openapi.json` at those commits, pinned by SHA for +reproducibility. There are no version *tags* in the repo, but `openapi.json` has +25 commits of history — we picked two a month apart. Notably **both are labeled +`info.version: 2.0.0`** despite 32 structural differences, which makes the +version-diff genuinely useful. + +The real diff turned out to be **backward-compatible** (only additions) — the +correct, honest result. So to demonstrate the breaking-change gate, we +**additionally** synthesize a breaking revision from the new spec via +`scripts/make_breaking_demo.py`, applying three controlled, clearly-logged deltas +(remove an endpoint, make a param required, narrow a type). This is the only +synthesized artifact; everything else is the real published spec. + +## Repo layout + +``` +specs/ vendored spec versions (old, new) + synthesized breaking demo +clients/ generated Python + TypeScript clients (git-ignored; make all rebuilds) +reports/ compat.md + compat.json (the deliverable) + breaking-demo/ +scripts/ fetch.sh, pipeline.sh, compat.py, make_breaking_demo.py +tests/ python/ (pytest) and typescript/ (tsx) smoke tests +mock/ (Prism runs from the spec directly) +.github/workflows/pipeline.yml CI: runs `make all`, gates on the report +Makefile one-command entry +``` + +## Explicit, defended cuts + +Scoped as a one-day build. What we deliberately left out and why: + +- **No real registry publishing** (PyPI / npm). We generate and *prove the + clients work*; publishing is a packaging/release concern orthogonal to the + spec-driven point, and it would need registry credentials. +- **Two languages** (Python + TypeScript). They prove the pattern; Go, Java, etc. + are each a one-line `openapi-generator` target, not new engineering. +- **Smoke test, not full contract coverage.** One representative typed call per + language (`GET /v1/users/me`) proves the generated client can build a request, + reach the server, and deserialize a typed response. Exhaustively testing all + 77 paths is volume, not signal, for one day. +- **Mock-only, no live API.** Prism from the spec keeps the whole pipeline + hermetic, keyless, and reproducible. No auth or integration against production. +- **Generated clients used as-is.** No hand-editing of generator output — that + is the entire value of spec-driven generation. + +## Secrets + +None required — the pipeline is mock-only. `.env` and `*.key` are git-ignored; +nothing secret is committed. + +## Attribution + +Built for the SuperDocs task. Uses the publicly published SuperDocs OpenAPI spec +(`docs.superdocs.app/openapi.json`, mirrored at `github.com/superdocsapp/docs`). diff --git a/extensions/doctask2-oviya-senthilkumar/mock/README.md b/extensions/doctask2-oviya-senthilkumar/mock/README.md new file mode 100644 index 00000000..ab16f9a0 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/mock/README.md @@ -0,0 +1,13 @@ +# Mock server + +There is no hand-written mock here — that's the point. The mock is **Stoplight +Prism**, run directly from the OpenAPI spec: + +```bash +docker run --rm -p 4010:4010 -v "$PWD:/tmp" \ + stoplight/prism:4 mock -h 0.0.0.0 /tmp/specs/superdocs-new.json +``` + +Prism serves schema-valid example responses for every operation in the spec, so +the generated clients can be smoke-tested with no API key and no live service. +`make all` starts and stops it automatically; `make mock` runs it standalone. diff --git a/extensions/doctask2-oviya-senthilkumar/package-lock.json b/extensions/doctask2-oviya-senthilkumar/package-lock.json new file mode 100644 index 00000000..9e1602ca --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/package-lock.json @@ -0,0 +1,907 @@ +{ + "name": "doctask2-oviya-senthilkumar", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "doctask2-oviya-senthilkumar", + "version": "1.0.0", + "devDependencies": { + "axios": "^1.7.7", + "tsx": "^4.19.2", + "typescript": "^5.6.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/tsx": { + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/extensions/doctask2-oviya-senthilkumar/package.json b/extensions/doctask2-oviya-senthilkumar/package.json new file mode 100644 index 00000000..141a40bf --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/package.json @@ -0,0 +1,14 @@ +{ + "name": "doctask2-oviya-senthilkumar", + "version": "1.0.0", + "private": true, + "description": "Spec-driven client generation pipeline for the SuperDocs API", + "scripts": { + "smoke:ts": "tsx tests/typescript/smoke.ts" + }, + "devDependencies": { + "axios": "^1.7.7", + "tsx": "^4.19.2", + "typescript": "^5.6.3" + } +} diff --git a/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json b/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json new file mode 100644 index 00000000..3a1f7685 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json @@ -0,0 +1,47 @@ +{ + "generated_at": "2026-08-19T12:25:30+00:00", + "base": { + "file": "superdocs-new.json", + "version": "2.0.0", + "title": "Universal Document AI API", + "paths": 77 + }, + "revision": { + "file": "superdocs-breaking.json", + "version": "2.0.0", + "title": "Universal Document AI API", + "paths": 76 + }, + "compatible": false, + "counts": { + "breaking": 3, + "warnings": 0, + "safe": 1, + "total": 4 + }, + "breaking": [ + { + "id": "request-parameter-became-required", + "endpoint": "POST /v1/chat", + "detail": "the `header` request parameter `authorization` became required" + }, + { + "id": "api-path-removed-without-deprecation", + "endpoint": "POST /v1/documents/export", + "detail": "api path removed without deprecation" + }, + { + "id": "request-parameter-type-changed", + "endpoint": "GET /v1/documents/{document_id}", + "detail": "for the `path` request parameter `document_id`, the `type` was changed from `string` to `integer`" + } + ], + "warnings": [], + "safe": [ + { + "id": "api-version-not-bumped", + "endpoint": "", + "detail": "a breaking change was detected but the version is still `2.0.0`" + } + ] +} \ No newline at end of file diff --git a/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md b/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md new file mode 100644 index 00000000..4494f2be --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md @@ -0,0 +1,27 @@ +# API Compatibility Report + +**❌ BREAKING CHANGES** + +- Base: `superdocs-new.json` — Universal Document AI API v2.0.0 (77 paths) +- Revision: `superdocs-breaking.json` — Universal Document AI API v2.0.0 (76 paths) +- Generated: 2026-08-19T12:25:30+00:00 + +**3 breaking**, 0 warning, 1 safe (4 total changes). + +> ⚠️ Both specs declare version **2.0.0**, yet the contract changed (4 differences). The version string alone does not signal compatibility — this diff does. + +## Breaking changes + +| Change | Endpoint | Detail | +|---|---|---| +| `request-parameter-became-required` | `POST /v1/chat` | the `header` request parameter `authorization` became required | +| `api-path-removed-without-deprecation` | `POST /v1/documents/export` | api path removed without deprecation | +| `request-parameter-type-changed` | `GET /v1/documents/{document_id}` | for the `path` request parameter `document_id`, the `type` was changed from `string` to `integer` | + + +## Safe / additive changes + +| Change | Endpoint | Detail | +|---|---|---| +| `api-version-not-bumped` | `` | a breaking change was detected but the version is still `2.0.0` | + diff --git a/extensions/doctask2-oviya-senthilkumar/reports/compat.json b/extensions/doctask2-oviya-senthilkumar/reports/compat.json new file mode 100644 index 00000000..f547d088 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/reports/compat.json @@ -0,0 +1,186 @@ +{ + "generated_at": "2026-08-19T12:25:29+00:00", + "base": { + "file": "superdocs-old.json", + "version": "2.0.0", + "title": "Universal Document AI API", + "paths": 65 + }, + "revision": { + "file": "superdocs-new.json", + "version": "2.0.0", + "title": "Universal Document AI API", + "paths": 77 + }, + "compatible": true, + "counts": { + "breaking": 0, + "warnings": 0, + "safe": 32, + "total": 32 + }, + "breaking": [], + "warnings": [], + "safe": [ + { + "id": "api-schema-removed", + "endpoint": "", + "detail": "removed the schema `RenameDocumentRequest`" + }, + { + "id": "endpoint-added", + "endpoint": "GET /.well-known/api-catalog", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "GET /.well-known/mcp-server-card", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "GET /.well-known/mcp.json", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "POST /v1/agents/adopt", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "GET /v1/agents/adopt-info", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "GET /v1/agents/challenge", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "POST /v1/agents/handoff", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "POST /v1/agents/request-upgrade", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "POST /v1/agents/signup", + "detail": "endpoint added" + }, + { + "id": "endpoint-added", + "endpoint": "GET /v1/agents/whoami", + "detail": "endpoint added" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/chat", + "detail": "added the new optional request property `deleted_part_chunk_ids`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/chat", + "detail": "added the new optional request property `touched_chunk_ids`" + }, + { + "id": "response-optional-property-added", + "endpoint": "POST /v1/chat", + "detail": "added the optional property `hint` to the response with the `200` status" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/chat/async", + "detail": "added the new optional request property `deleted_part_chunk_ids`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/chat/async", + "detail": "added the new optional request property `touched_chunk_ids`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/documents/export", + "detail": "added the new optional request property `options/fidelity`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/documents/export", + "detail": "added the new optional request property `source_filename`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/documents/export/email-request", + "detail": "added the new optional request property `source_filename`" + }, + { + "id": "endpoint-added", + "endpoint": "POST /v1/documents/images/upload-base64", + "detail": "endpoint added" + }, + { + "id": "new-optional-request-property", + "endpoint": "PATCH /v1/documents/{document_id}", + "detail": "added the new optional request property `base_parts`" + }, + { + "id": "new-optional-request-property", + "endpoint": "PATCH /v1/documents/{document_id}", + "detail": "added the new optional request property `parts`" + }, + { + "id": "request-property-became-optional", + "endpoint": "PATCH /v1/documents/{document_id}", + "detail": "the request property `title` became optional" + }, + { + "id": "request-property-list-of-types-widened", + "endpoint": "PATCH /v1/documents/{document_id}", + "detail": "request property `title` list-of-types was widened by adding types `null` to media type `application/json`" + }, + { + "id": "request-property-min-length-decreased", + "endpoint": "PATCH /v1/documents/{document_id}", + "detail": "the `title` request property's minLength was decreased from `1` to `0`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/downloads", + "detail": "added the new optional request property `options/fidelity`" + }, + { + "id": "endpoint-added", + "endpoint": "POST /v1/limits/increase-request", + "detail": "endpoint added" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/sessions/{session_id}/documents/{document_id}/save", + "detail": "added the new optional request property `deleted_part_chunk_ids`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/sessions/{session_id}/documents/{document_id}/save", + "detail": "added the new optional request property `touched_chunk_ids`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/sessions/{session_id}/documents/{document_id}/unarchive", + "detail": "added the new optional request property `anyOf[subschema #1: SaveDocumentRequest]/deleted_part_chunk_ids`" + }, + { + "id": "new-optional-request-property", + "endpoint": "POST /v1/sessions/{session_id}/documents/{document_id}/unarchive", + "detail": "added the new optional request property `anyOf[subschema #1: SaveDocumentRequest]/touched_chunk_ids`" + }, + { + "id": "response-optional-property-added", + "endpoint": "POST /v1/uploads/{upload_id}/process", + "detail": "added the optional property `warnings` to the response with the `200` status" + } + ] +} \ No newline at end of file diff --git a/extensions/doctask2-oviya-senthilkumar/reports/compat.md b/extensions/doctask2-oviya-senthilkumar/reports/compat.md new file mode 100644 index 00000000..1a326c5b --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/reports/compat.md @@ -0,0 +1,54 @@ +# API Compatibility Report + +**✅ BACKWARD COMPATIBLE** + +- Base: `superdocs-old.json` — Universal Document AI API v2.0.0 (65 paths) +- Revision: `superdocs-new.json` — Universal Document AI API v2.0.0 (77 paths) +- Generated: 2026-08-19T12:25:29+00:00 + +**0 breaking**, 0 warning, 32 safe (32 total changes). + +> ⚠️ Both specs declare version **2.0.0**, yet the contract changed (32 differences). The version string alone does not signal compatibility — this diff does. + +## Breaking changes + +_none_ + + +## Safe / additive changes + +| Change | Endpoint | Detail | +|---|---|---| +| `api-schema-removed` | `` | removed the schema `RenameDocumentRequest` | +| `endpoint-added` | `GET /.well-known/api-catalog` | endpoint added | +| `endpoint-added` | `GET /.well-known/mcp-server-card` | endpoint added | +| `endpoint-added` | `GET /.well-known/mcp.json` | endpoint added | +| `endpoint-added` | `POST /v1/agents/adopt` | endpoint added | +| `endpoint-added` | `GET /v1/agents/adopt-info` | endpoint added | +| `endpoint-added` | `GET /v1/agents/challenge` | endpoint added | +| `endpoint-added` | `POST /v1/agents/handoff` | endpoint added | +| `endpoint-added` | `POST /v1/agents/request-upgrade` | endpoint added | +| `endpoint-added` | `POST /v1/agents/signup` | endpoint added | +| `endpoint-added` | `GET /v1/agents/whoami` | endpoint added | +| `new-optional-request-property` | `POST /v1/chat` | added the new optional request property `deleted_part_chunk_ids` | +| `new-optional-request-property` | `POST /v1/chat` | added the new optional request property `touched_chunk_ids` | +| `response-optional-property-added` | `POST /v1/chat` | added the optional property `hint` to the response with the `200` status | +| `new-optional-request-property` | `POST /v1/chat/async` | added the new optional request property `deleted_part_chunk_ids` | +| `new-optional-request-property` | `POST /v1/chat/async` | added the new optional request property `touched_chunk_ids` | +| `new-optional-request-property` | `POST /v1/documents/export` | added the new optional request property `options/fidelity` | +| `new-optional-request-property` | `POST /v1/documents/export` | added the new optional request property `source_filename` | +| `new-optional-request-property` | `POST /v1/documents/export/email-request` | added the new optional request property `source_filename` | +| `endpoint-added` | `POST /v1/documents/images/upload-base64` | endpoint added | +| `new-optional-request-property` | `PATCH /v1/documents/{document_id}` | added the new optional request property `base_parts` | +| `new-optional-request-property` | `PATCH /v1/documents/{document_id}` | added the new optional request property `parts` | +| `request-property-became-optional` | `PATCH /v1/documents/{document_id}` | the request property `title` became optional | +| `request-property-list-of-types-widened` | `PATCH /v1/documents/{document_id}` | request property `title` list-of-types was widened by adding types `null` to media type `application/json` | +| `request-property-min-length-decreased` | `PATCH /v1/documents/{document_id}` | the `title` request property's minLength was decreased from `1` to `0` | +| `new-optional-request-property` | `POST /v1/downloads` | added the new optional request property `options/fidelity` | +| `endpoint-added` | `POST /v1/limits/increase-request` | endpoint added | +| `new-optional-request-property` | `POST /v1/sessions/{session_id}/documents/{document_id}/save` | added the new optional request property `deleted_part_chunk_ids` | +| `new-optional-request-property` | `POST /v1/sessions/{session_id}/documents/{document_id}/save` | added the new optional request property `touched_chunk_ids` | +| `new-optional-request-property` | `POST /v1/sessions/{session_id}/documents/{document_id}/unarchive` | added the new optional request property `anyOf[subschema #1: SaveDocumentRequest]/deleted_part_chunk_ids` | +| `new-optional-request-property` | `POST /v1/sessions/{session_id}/documents/{document_id}/unarchive` | added the new optional request property `anyOf[subschema #1: SaveDocumentRequest]/touched_chunk_ids` | +| `response-optional-property-added` | `POST /v1/uploads/{upload_id}/process` | added the optional property `warnings` to the response with the `200` status | + diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/compat.py b/extensions/doctask2-oviya-senthilkumar/scripts/compat.py new file mode 100644 index 00000000..be7819cb --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/scripts/compat.py @@ -0,0 +1,140 @@ +""" +Compatibility report generator — the centerpiece. + +Consumes oasdiff's JSON changelog for a (base -> revision) spec pair and produces: + - reports/compat.md a clean human-readable report + - reports/compat.json a structured summary (for machines / CI) + +It classifies every change as BREAKING or SAFE using oasdiff's severity level +(3 = breaking/error, 2 = warning, 1 = info/additive) and, with --gate, exits +non-zero when any breaking change is present — so CI fails on an incompatible +spec bump. + +Usage: + python scripts/compat.py --changelog reports/changelog.json \ + --base specs/superdocs-old.json --revision specs/superdocs-new.json \ + --out reports [--gate] +""" +import argparse +import json +import os +import sys +from datetime import datetime, timezone + +LEVEL_BREAKING = 3 +LEVEL_WARNING = 2 + + +def _spec_meta(path): + try: + d = json.load(open(path, encoding="utf-8")) + return { + "file": os.path.basename(path), + "version": d.get("info", {}).get("version", "?"), + "title": d.get("info", {}).get("title", "?"), + "paths": len(d.get("paths", {})), + } + except Exception: + return {"file": os.path.basename(path), "version": "?", "title": "?", "paths": 0} + + +def _row(c): + ep = f"{c.get('operation', '')} {c.get('path', '')}".strip() + return {"id": c.get("id", ""), "endpoint": ep, "detail": c.get("text", "")} + + +def build(changelog, base, revision): + breaking = [_row(c) for c in changelog if c.get("level") == LEVEL_BREAKING] + warnings = [_row(c) for c in changelog if c.get("level") == LEVEL_WARNING] + safe = [_row(c) for c in changelog if c.get("level") not in (LEVEL_BREAKING, LEVEL_WARNING)] + + base_m, rev_m = _spec_meta(base), _spec_meta(revision) + compatible = len(breaking) == 0 + + summary = { + "generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), + "base": base_m, + "revision": rev_m, + "compatible": compatible, + "counts": { + "breaking": len(breaking), + "warnings": len(warnings), + "safe": len(safe), + "total": len(changelog), + }, + "breaking": breaking, + "warnings": warnings, + "safe": safe, + } + return summary + + +def _table(rows): + if not rows: + return "_none_\n" + out = ["| Change | Endpoint | Detail |", "|---|---|---|"] + for r in rows: + detail = r["detail"].replace("|", "\\|") + out.append(f"| `{r['id']}` | `{r['endpoint']}` | {detail} |") + return "\n".join(out) + "\n" + + +def to_markdown(s): + b, r = s["base"], s["revision"] + verdict = "✅ BACKWARD COMPATIBLE" if s["compatible"] else "❌ BREAKING CHANGES" + c = s["counts"] + same_version = b["version"] == r["version"] + lines = [ + "# API Compatibility Report", + "", + f"**{verdict}**", + "", + f"- Base: `{b['file']}` — {b['title']} v{b['version']} ({b['paths']} paths)", + f"- Revision: `{r['file']}` — {r['title']} v{r['version']} ({r['paths']} paths)", + f"- Generated: {s['generated_at']}", + "", + f"**{c['breaking']} breaking**, {c['warnings']} warning, {c['safe']} safe " + f"({c['total']} total changes).", + "", + ] + if same_version and c["total"] > 0: + lines += [ + f"> ⚠️ Both specs declare version **{b['version']}**, yet the contract changed " + f"({c['total']} differences). The version string alone does not signal " + "compatibility — this diff does.", + "", + ] + lines += ["## Breaking changes", "", _table(s["breaking"]), ""] + if s["warnings"]: + lines += ["## Warnings", "", _table(s["warnings"]), ""] + lines += ["## Safe / additive changes", "", _table(s["safe"]), ""] + return "\n".join(lines) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--changelog", required=True) + ap.add_argument("--base", required=True) + ap.add_argument("--revision", required=True) + ap.add_argument("--out", default="reports") + ap.add_argument("--gate", action="store_true", + help="exit non-zero if any breaking change is present (for CI)") + args = ap.parse_args() + + changelog = json.load(open(args.changelog, encoding="utf-8")) + summary = build(changelog, args.base, args.revision) + + os.makedirs(args.out, exist_ok=True) + json.dump(summary, open(os.path.join(args.out, "compat.json"), "w", encoding="utf-8"), indent=2) + open(os.path.join(args.out, "compat.md"), "w", encoding="utf-8").write(to_markdown(summary)) + + n = summary["counts"]["breaking"] + verdict = "compatible" if n == 0 else f"{n} BREAKING change(s)" + print(f"compat report: {verdict} — wrote {args.out}/compat.md and compat.json") + + if args.gate and n > 0: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/fetch.sh b/extensions/doctask2-oviya-senthilkumar/scripts/fetch.sh new file mode 100644 index 00000000..58960372 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/scripts/fetch.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Fetch the two pinned spec versions from the published SuperDocs docs repo and +# (re)generate the breaking-demo spec. Pinned by commit SHA for reproducibility. +# If offline, keeps the vendored copies already committed to specs/. +set -euo pipefail +cd "$(dirname "$0")/.." + +OLD_SHA=a8351de # 2026-06-24 (65 paths) +NEW_SHA=ac0d44c # 2026-07-23 (77 paths) — latest openapi.json commit + +fetch_spec() { + local url="$1" out="$2" + if curl -fsSL "$url" -o "$out.tmp" 2>/dev/null && [ -s "$out.tmp" ]; then + mv "$out.tmp" "$out"; echo " fetched $out" + else + rm -f "$out.tmp"; echo " (offline) kept vendored $out" + fi +} + +mkdir -p specs +echo "fetching pinned specs from github.com/superdocsapp/docs ..." +fetch_spec "https://raw.githubusercontent.com/superdocsapp/docs/${OLD_SHA}/openapi.json" specs/superdocs-old.json +fetch_spec "https://raw.githubusercontent.com/superdocsapp/docs/${NEW_SHA}/openapi.json" specs/superdocs-new.json +python scripts/make_breaking_demo.py diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py b/extensions/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py new file mode 100644 index 00000000..d3b2282b --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py @@ -0,0 +1,73 @@ +""" +Synthesize a deliberately BREAKING revision of the current spec, to demonstrate +that the compatibility gate catches breaking changes and fails CI. + +The real published history (old a8351de -> new ac0d44c) is backward-compatible +(only additions), which is the correct, authentic result. This script derives a +hypothetical "next" spec from the NEW spec by applying three controlled deltas — +each a classic breaking change — so we can prove the gate flags them: + + 1. remove an existing endpoint (clients calling it now 404) + 2. make an optional parameter required (existing clients omit it -> 400) + 3. narrow a parameter's type (string -> integer: old values rejected) + +It prints exactly what it changed. Output: specs/superdocs-breaking.json +""" +import json +import os + +HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +NEW = os.path.join(HERE, "specs", "superdocs-new.json") +OUT = os.path.join(HERE, "specs", "superdocs-breaking.json") + +spec = json.load(open(NEW, encoding="utf-8")) +spec = json.loads(json.dumps(spec)) # deep copy +paths = spec["paths"] +changes = [] + +_METHODS = ("get", "post", "put", "delete", "patch") + +# 1) Remove an endpoint (prefer a well-known one so the report reads clearly). +for candidate in ["/v1/documents/export", "/v1/chat/{session_id}/approve", + "/v1/documents/upload", "/v1/chat"]: + if candidate in paths: + del paths[candidate] + changes.append(f"removed endpoint {candidate}") + break + +# 2) Make the first optional parameter we find required. +def _first_optional_param(): + for p, item in paths.items(): + for m, op in item.items(): + if m not in _METHODS or not isinstance(op, dict): + continue + for param in op.get("parameters", []): + if not param.get("required", False): + return p, m, param + return None, None, None + +p, m, param = _first_optional_param() +if param is not None: + param["required"] = True + changes.append(f"made parameter '{param.get('name')}' required on {m.upper()} {p}") + +# 3) Narrow a string parameter's type to integer. +def _first_string_param(): + for p, item in paths.items(): + for m, op in item.items(): + if m not in _METHODS or not isinstance(op, dict): + continue + for param in op.get("parameters", []): + if param.get("schema", {}).get("type") == "string": + return p, m, param + return None, None, None + +p, m, param = _first_string_param() +if param is not None: + param["schema"]["type"] = "integer" + changes.append(f"narrowed type of '{param.get('name')}' (string->integer) on {m.upper()} {p}") + +json.dump(spec, open(OUT, "w", encoding="utf-8"), indent=2) +print(f"Wrote {os.path.relpath(OUT, HERE)} with {len(changes)} breaking delta(s):") +for c in changes: + print(" -", c) diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/pipeline.sh b/extensions/doctask2-oviya-senthilkumar/scripts/pipeline.sh new file mode 100644 index 00000000..6e63a796 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/scripts/pipeline.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# One-command pipeline: fetch -> generate -> compat -> mock -> smoke. +# Portable: runs on Linux/macOS/CI and on Windows Git Bash. Tools (oasdiff, +# openapi-generator, prism) run in Docker so a clean machine needs only Docker, +# plus python3 + node to run the smoke tests. +set -euo pipefail +cd "$(dirname "$0")/.." +ROOT="$(pwd)" +export MSYS_NO_PATHCONV=1 # harmless on Linux; needed for Docker -v on Git Bash + +GEN_IMG=openapitools/openapi-generator-cli:v7.12.0 +OASDIFF_IMG=tufin/oasdiff +PRISM_IMG=stoplight/prism:4 +OLD=specs/superdocs-old.json +NEW=specs/superdocs-new.json +BREAKING=specs/superdocs-breaking.json +MOCK_URL=${SUPERDOCS_MOCK_URL:-http://localhost:4010} + +oasdiff() { docker run --rm -v "${ROOT}:/work" "$OASDIFF_IMG" "$@"; } +gen() { docker run --rm -v "${ROOT}:/local" "$GEN_IMG" "$@"; } + +echo "== 0/5 fetch pinned specs ==" +bash scripts/fetch.sh + +echo "== 1/5 generate typed clients (openapi-generator) ==" +gen generate -i "/local/$NEW" -g python -o /local/clients/python \ + --additional-properties=packageName=superdocs_client,projectName=superdocs-client,library=urllib3 \ + --skip-validate-spec >/dev/null +gen generate -i "/local/$NEW" -g typescript-axios -o /local/clients/typescript \ + --additional-properties=npmName=superdocs-client,supportsES6=true,withSeparateModelsAndApi=true,apiPackage=api,modelPackage=models \ + --skip-validate-spec >/dev/null +echo " python: $(find clients/python -type f | wc -l | tr -d ' ') files | typescript: $(find clients/typescript -type f | wc -l | tr -d ' ') files" + +echo "== 2/5 compatibility report (old -> new) ==" +oasdiff changelog -f json "/work/$OLD" "/work/$NEW" > reports/changelog.json 2>/dev/null +python scripts/compat.py --changelog reports/changelog.json --base "$OLD" --revision "$NEW" --out reports --gate + +echo " breaking-gate demo (new -> synthesized breaking) —" +oasdiff changelog -f json "/work/$NEW" "/work/$BREAKING" > reports/changelog-breaking.json 2>/dev/null +if python scripts/compat.py --changelog reports/changelog-breaking.json --base "$NEW" --revision "$BREAKING" --out reports/breaking-demo --gate; then + echo " ERROR: gate did NOT fail on breaking changes"; exit 1 +else + echo " gate correctly returned non-zero on breaking changes (CI would fail)" +fi + +echo "== 3/5 mock server (prism) ==" +docker rm -f superdocs-mock >/dev/null 2>&1 || true +docker run -d --rm --name superdocs-mock -p 4010:4010 -v "${ROOT}:/tmp" "$PRISM_IMG" mock -h 0.0.0.0 "/tmp/$NEW" >/dev/null +trap 'docker rm -f superdocs-mock >/dev/null 2>&1 || true' EXIT +for i in $(seq 1 30); do curl -fsS -o /dev/null "$MOCK_URL/v1/users/me" 2>/dev/null && break; sleep 1; done +echo " prism serving $NEW at $MOCK_URL" + +echo "== 4/5 python smoke (generated client -> mock) ==" +[ -d .venv ] || python -m venv .venv +PYBIN=.venv/Scripts/python.exe; [ -x "$PYBIN" ] || PYBIN=.venv/bin/python +"$PYBIN" -m pip install -q --upgrade pip >/dev/null +"$PYBIN" -m pip install -q -e clients/python pytest >/dev/null +SUPERDOCS_MOCK_URL="$MOCK_URL" "$PYBIN" -m pytest tests/python -q + +echo "== 5/5 typescript smoke (generated client -> mock) ==" +[ -d node_modules ] || npm install --silent +SUPERDOCS_MOCK_URL="$MOCK_URL" npx tsx tests/typescript/smoke.ts + +echo "" +echo "PIPELINE OK — clients generated, compat report written, gate verified, both smoke tests passed." diff --git a/extensions/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json b/extensions/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json new file mode 100644 index 00000000..27fc3c7e --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json @@ -0,0 +1,8441 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Universal Document AI API", + "description": "AI-powered document editing with multi-tenant organization support", + "version": "2.0.0" + }, + "paths": { + "/v1/chat": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Edit, draft, or restructure a document using natural language. Preserves tables, styling, and formatting.", + "description": "Synchronous AI chat that can rewrite specific paragraphs, add or remove table rows, restructure sections, generate new content from templates, or transform an entire document. Pass document_html only to load or replace the document; once a session holds a document the server persists it across turns, so omit it on follow-up turns. To replace the whole document with EXACT content you already hold, pass that content in document_html (a verbatim load \u2014 the AI never re-types it) or upload it via upload_document_base64; describing the content only in `message` makes the AI author it, which can drift from your text. Returns AI response text plus structural document changes (HTML edits, additions, deletions) with chunk IDs. One billable operation per document-modifying turn; very large multi-section edits bill one operation per 25 sections changed. For long-running edits or human-in-the-loop approval, use chat_async. Optional: model_tier (core/turbo/pro/max), thinking_depth (fast/balanced/deep), image_attachments for multimodal vision. RECOMMENDED for AI agents working with large documents (>20 pages): set response_mode='compact' to skip the full HTML in the response (the AI returns only per-section diffs in chunk_diffs) and save thousands of tokens per turn. To read sections in compact mode, just send a natural-language request like 'show me the force majeure section' \u2014 the AI returns the content in the reply text. Always use natural language to describe what you want; the AI handles all internal section lookups.", + "operationId": "chat", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": true, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UniversalChatRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "List your active document editing sessions to resume or audit prior work.", + "description": "Returns sessions sorted by most recent activity, with message counts and last-updated timestamps. Each session represents one document with full edit history and AI conversation persisted server-side. Use to find a previous editing context to resume (then call get_session_history) or to audit your workspace.", + "operationId": "list_sessions", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter by user ID (deprecated - auth determines scope)", + "title": "User Id" + }, + "description": "Filter by user ID (deprecated - auth determines scope)" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of sessions to return", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of sessions to return" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents": { + "get": { + "tags": [ + "v1", + "mcp" + ], + "summary": "List your saved documents (the Files view).", + "description": "List the authenticated user/org's saved documents, most-recently-updated first\n(metadata only \u2014 no document content). Each carries a session_count (\"N chats\"). Documents\nbecome durable + reusable automatically as you create or edit them; on the FIRST call we also\none-time import the documents of pre-Files-view chats so prior work appears here too.", + "operationId": "list_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1, + "description": "Maximum number of documents to return.", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of documents to return." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Offset for pagination.", + "default": 0, + "title": "Offset" + }, + "description": "Offset for pagination." + }, + { + "name": "include_preview", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include a small preview_html (first chunks) per document for thumbnails. The web Files view sets this; agents leave it off to stay token-light.", + "default": false, + "title": "Include Preview" + }, + "description": "Include a small preview_html (first chunks) per document for thumbnails. The web Files view sets this; agents leave it off to stay token-light." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/cross-session-memory": { + "delete": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Clear your cross-session memory note.", + "description": "Delete the caller's cross-session memory note. Owner-scoped: a caller can only clear its OWN\nnote. With no key this clears the account-level note; with a memory_key it clears that one\nend-customer's note. Idempotent (removed=0 if it didn't exist).", + "operationId": "clear_cross_session_memory", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "memory_key", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Clear ONE end-customer's stored memory (the cross_session_memory_key you send on chat). Omit to clear the account-level note (the B2C default).", + "title": "Memory Key" + }, + "description": "Clear ONE end-customer's stored memory (the cross_session_memory_key you send on chat). Omit to clear the account-level note (the B2C default)." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/{document_id}": { + "get": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Get a saved document's detail, structure outline, and the chats that used it.", + "description": "One saved document's metadata plus a STRUCTURE outline and the chat sessions that have it\nopen (prior-chats-per-file). Owner-scoped \u2014 404 if the document isn't yours.\n\n`structure` (always included, token-light, NON-BILLABLE) is the cheap verify step after\nany edit: `headings` (level + text + position), `section_count` (heading-anchored), `block_count`\n(editable chunks), `media` (image/diagram counts). It answers \"did my edit land?\" for free \u2014\nnever export a whole document just to check its structure. Pass include_html=true only when\nyou need the body.", + "operationId": "get_document_detail", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Document Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, also returns the document's full reassembled HTML body (`html`), plus its `page_setup` and `version_id`. Default false returns metadata + structure + prior-chats only, keeping the response token-light. This is the by-id way to read a saved document's body without opening it into a session.", + "default": false, + "title": "Include Html" + }, + "description": "When true, also returns the document's full reassembled HTML body (`html`), plus its `page_setup` and `version_id`. Default false returns metadata + structure + prior-chats only, keeping the response token-light. This is the by-id way to read a saved document's body without opening it into a session." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "patch": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Rename a saved document and/or update its out-of-flow parts (headers, footers, footnotes, endnotes, comments, sections).", + "description": "Rename one of your saved documents and/or update its out-of-flow parts. Parts are\nthe document's out-of-flow content \u2014 headers/footers, footnote and endnote bodies,\ncomments, and per-section page geometry. Every fragment is sanitized on write, writes\nare versioned and safe under concurrent editors, and parts revert with the document.\nA title-only call behaves exactly as the original rename (response `status:\"renamed\"`).\nPart content lives in the document body and is edited exactly like any other document\ncontent (via chat or the document write endpoints); a legacy `parts` payload returns\n400 `parts_moved_to_chunks` pointing you there.", + "operationId": "rename_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDocumentRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Delete (archive) a saved document.", + "description": "Soft-archive a saved document \u2014 it leaves the Files view but is recoverable server-side.\nPresence-aware: if the document is currently open in ANOTHER session and `force` is not set,\nthe request FAILS honestly with HTTP 409 (`code:\"document_in_use\"` + `open_in_sessions:N` +\n`suggested_action`) so callers can confirm \"open in N other session(s) \u2014 delete anyway?\" and\nre-call with `force=true`. On `force=true` it archives, unlinks every session, and notifies the\nother sessions (which then prompt the user to keep editing or honor the deletion).\n\nA no-op never wears a success status: if nothing was archived you get the 409 above, so a\n2xx always means the archive actually happened.", + "operationId": "archive_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "force", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Confirm deletion of a doc that is open in another session. Without it, deleting an in-use doc fails with HTTP 409 (code document_in_use, open_in_sessions:N) instead of tombstoning \u2014 nothing is archived until you re-call with force=true.", + "default": false, + "title": "Force" + }, + "description": "Confirm deletion of a doc that is open in another session. Without it, deleting an in-use doc fails with HTTP 409 (code document_in_use, open_in_sessions:N) instead of tombstoning \u2014 nothing is archived until you re-call with force=true." + }, + { + "name": "from_session", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The caller's own session id (so it is excluded from the in-use check). Omit for a pure Files-view delete.", + "title": "From Session" + }, + "description": "The caller's own session id (so it is excluded from the in-use check). Omit for a pure Files-view delete." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/{document_id}/unarchive": { + "post": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Restore (un-archive) a previously archived document by its id.", + "description": "Restore (un-archive) a saved document by its id \u2014 the mirror of archive_document. The document\nre-enters your Files view and can be opened/edited again (open it into a chat with open_documents).\n`document_id` is the same id used by list_documents / archive_document. Idempotent: restoring an\nalready-active (or unknown) document is a no-op. Non-billable.", + "operationId": "unarchive_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/open": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Open saved documents into a chat session (shared, never copied).", + "description": "Load one or more SAVED documents into a session as editable tabs. The SAME durable\ndocument is attached (never copied), so edits flow back to the one shared row with\ncross-session soft-collaboration. The first listed document is focused. Returns the\nrefreshed document roster so the client can render tabs immediately.", + "operationId": "open_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenDocumentsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/init": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Start a new chat session and open documents into it in one call.", + "description": "Create a session AND open N saved documents into it in ONE call (the documents are SHARED,\nnever copied \u2014 the same cross-session behavior as /documents/open). The web UI passes its own\nsession_id so its session model stays consistent; an MCP/API integrator can omit it and the\nserver mints one \u2014 the one-call way to start a session with documents already open. With no\ndocument_ids it just returns a fresh, empty session. The open semantics (first document focused,\ndurable binding, returned roster) are identical to /documents/open.", + "operationId": "init_session", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InitSessionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/save": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Persist a user-edited document (non-AI autosave).", + "description": "Persist a HUMAN-edited document \u2014 the editor's debounced autosave + save-on-blur \u2014 WITHOUT\nan AI turn. Re-indexes the html into the target document (preserving its durable identity so it\nUPDATES the same Files entry), then saves it so pure typing is preserved AND other sessions with\nthe same document open stay in sync. The AI-edit flow is unaffected (autosave saves first; a\nlater AI result is merged with your saved edits). Non-billable, REST-only (a UI affordance, not an MCP tool).", + "operationId": "save_human_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SaveDocumentRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/unarchive": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Restore a document another session archived, keeping your current edits (web app).", + "description": "Restore (un-archive) a document that was archived, re-linking it to this session and bringing\nits content back. `document_id` is the session-local id (same as /save); the durable id is\nrecovered from the cached document. Idempotent: restoring an already-active (or never-archived)\ndocument is a no-op. Non-billable.\n\nTwo shapes: WITHOUT a body (e.g. an AI agent restoring a document by id) the archived content is\nrestored as-is; WITH a body carrying the current editor HTML (the web app's \"keep editing\n(restores it)\" choice after another session deleted the document) the document is restored AND the\nsupplied edits are saved on top, so it converges for everyone.", + "operationId": "restore_document_keep_editing", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SaveDocumentRequest" + }, + { + "type": "null" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/blank": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Open a new blank document as a tab in the session.", + "description": "Open a fresh BLANK document as a new focused tab in the session (the tab-strip \"+\").\nNon-billable \u2014 no AI, no upload pipeline; it is saved on first edit.\nREST-only (a manual UI affordance, not an MCP tool \u2014 agents create documents through chat).", + "operationId": "new_blank_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "List the documents open in a multi-document session.", + "description": "Returns the editable documents open in this session (focused first), each with its id, chunk\ncount, and focused flag \u2014 so a client can render document tabs. The response is token-light by\ndefault; pass include_html=true to also get each document's reassembled HTML.\n\nID MAPPING: `document_id` is the SESSION-LOCAL slot id (\"doc_primary\", \"doc_ab12\u2026\");\n`durable_document_id` is the PERMANENT documents.id UUID \u2014 the SAME id `list_documents` (Files)\nshows and `get_document_detail` / `rename_document` / `archive_document` / `open_documents`\ntake (null until the document's first save). focus_session_document, close_session_document,\nand chat's `document_id` accept EITHER form, so you can drive a session entirely with durable\nids.", + "operationId": "list_session_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "report_changed", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Resume awareness: when true, each doc carries `changed_since_seen` = its committed version is newer than the version THIS session last saw (changed by another session while away). The web UI sets this ONLY on resume / a chat switch (never on the ~2s poll) so it can show a one-time 'changed since you were last here' notice \u2014 ongoing changes are surfaced through the normal change feed. Computed before the roster updates the version this session has seen.", + "default": false, + "title": "Report Changed" + }, + "description": "Resume awareness: when true, each doc carries `changed_since_seen` = its committed version is newer than the version THIS session last saw (changed by another session while away). The web UI sets this ONLY on resume / a chat switch (never on the ~2s poll) so it can show a one-time 'changed since you were last here' notice \u2014 ongoing changes are surfaced through the normal change feed. Computed before the roster updates the version this session has seen." + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/doc-events": { + "get": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Session Document Events", + "description": "Poll for cross-session document updates: returns `document_updated` events for documents\nTHIS session holds that OTHER sessions committed since `after_id`. The client polls this\n(~every 1-2s while a doc is open) and re-fetches the changed document's content so all open\nsessions converge without a refresh. Excludes this session's own writes by default (the web UI\nalready has its own changes); a REST/MCP integrator can pass `include_own=true` to receive its\nown events too. REST-only \u2014 a lightweight poll, deliberately NOT an MCP tool and NOT a held\nSSE connection (keeps the per-instance connection budget free).", + "operationId": "session_document_events_v1_sessions__session_id__doc_events_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "after_id", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Return events with id greater than this (the poll cursor).", + "default": 0, + "title": "After Id" + }, + "description": "Return events with id greater than this (the poll cursor)." + }, + { + "name": "include_own", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Integrator opt-out: when true, ALSO return this session's own document_updated events (default false excludes them \u2014 the web UI never needs its own writes echoed). A REST/MCP integration that writes on one connection and polls on another, or wants a complete change feed for the docs it holds, sets this true.", + "default": false, + "title": "Include Own" + }, + "description": "Integrator opt-out: when true, ALSO return this session's own document_updated events (default false excludes them \u2014 the web UI never needs its own writes echoed). A REST/MCP integration that writes on one connection and polls on another, or wants a complete change feed for the docs it holds, sets this true." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/focus": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Switch which document is focused in a multi-document session.", + "description": "Make document_id the focused document (the editor's active document and the default edit\ntarget). Accepts either the session slot id OR the durable documents.id UUID (the id shown by\nlist_documents/Files). Persists the outgoing focused document into the session's\ndocument map and returns the now-focused document's HTML.", + "operationId": "focus_session_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Close (remove) a document from a multi-document session.", + "description": "Remove document_id from the session's open documents (the tab close button). If it's the\nfocused document, focus another open document \u2014 `next_focus` if it's still open (so the client\ncan pick the adjacent tab), else the next remaining one; closing the last document leaves the\nsession empty. Returns the updated document roster + the now-focused document's HTML.\n\nThe close takes effect immediately and is persisted, so a later reconnect or session restore\nwon't bring the closed document back. Persistence is best-effort \u2014 a failure is logged, not\nfatal (the close still holds for the active session).", + "operationId": "close_session_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "next_focus", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Next Focus" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/chunks/{chunk_id}/re-edit": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Re-apply the AI's intended edit on top of the user's current version of one section.", + "description": "Resolve a concurrent-edit conflict on ONE section. When a user edited a section while the AI\nwas also changing it, this re-applies the AI's intended change on top of the user's current text\n(mode=\"redo\"), or blends the user's and AI's versions into one (mode=\"merge\"). Returns only the\nrewritten section HTML. Performs one AI edit and counts as one billable operation.", + "operationId": "re_edit_chunk_on_user_version", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "chunk_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Chunk Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReEditChunkRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReEditChunkResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/history": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Restore the full conversation and document state for a previous session.", + "description": "Returns complete message history (user and AI), final document HTML with chunk IDs preserved, attachment list, and editor actions (font, color, alignment changes). Use to continue editing a document you started in a prior session. The AI rehydrates with full context of all prior decisions, chunk IDs, and attachments. Pass include_document_html=false to skip the full document body when you only need the conversation.", + "operationId": "get_session_history", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "focus_document_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Durable id of the document to focus on restore (e.g. the file the user clicked in the Files view). When set, that document is shown in the editor and marked focused in the roster; omit to use the session's own last-focused document.", + "title": "Focus Document Id" + }, + "description": "Durable id of the document to focus on restore (e.g. the file the user clicked in the Files view). When set, that document is shown in the editor and marked focused in the roster; omit to use the session's own last-focused document." + }, + { + "name": "include_document_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When false, omit the restored document's full HTML body (document_state.html_content) to keep your context small \u2014 agents that just need the conversation + metadata should set false. Default true (the web app needs the body to re-render the editor).", + "default": true, + "title": "Include Document Html" + }, + "description": "When false, omit the restored document's full HTML body (document_state.html_content) to keep your context small \u2014 agents that just need the conversation + metadata should set false. Default true (the web app needs the body to re-render the editor)." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnhancedChatHistoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/revert": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Rewind a chat session to before a specific user message \u2014 restores both the document and the conversation in one call.", + "description": "Rewinds a chat session to the state immediately before a specific user message. The document, conversation history, and supporting context all snap back to that point. The text of the reverted message is returned (compose_text) so the caller can edit and resend it. Messages from the reverted turn forward are soft-archived: hidden from active reads but retained for audit. The original conversation is preserved server-side; the restored conversation becomes the active timeline. Rejects with 409 if the session has a chat job in progress or awaiting approval \u2014 wait for the job to settle before reverting. Returns 422 if the message predates the revertability feature.", + "operationId": "revert_session_to_message", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/redo": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Undo a revert: restore the pre-revert state and the rolled-back turns.", + "description": "Revert is non-destructive: this restores the session FORWARD to the pre-revert state\n(returned by the revert as redo_checkpoint_id), restores the document to that state while still\nmerging any concurrent edits from other sessions (keeping both where they conflict), and\nun-archives exactly the chat turns the revert hid. Intended for use immediately after a revert;\nonce a new message is sent the timeline diverges and the web app stops offering it.", + "operationId": "redo_revert", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedoRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/upload": { + "post": { + "tags": [ + "v1", + "attachments" + ], + "summary": "Upload Attachment", + "description": "Upload a document attachment to a session for AI reference.\n\nThe file is processed asynchronously \u2014 text is extracted, converted, and indexed\nso the AI can search and reference it during chat. Use GET /v1/attachments/status/{session_id}\nto check processing progress.\n\nSupported file types: .pdf, .docx, .txt, .rtf, .md, .html, .htm (max 50 MB).\nReturns a job_id for tracking the processing status.", + "operationId": "upload_attachment", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_attachment" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/{attachment_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Remove an attachment from a session or cancel its in-progress processing.", + "description": "Removes the attachment from the session; the AI will no longer reference it in subsequent chat turns. If processing is still in progress, also cancels the underlying job. Use to free up context, remove sensitive files mid-session, or replace a stale reference document. The attachment_id can be either the final attachment ID or the job ID (during processing).", + "operationId": "delete_attachment", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "attachment_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Attachment Id" + } + }, + { + "name": "session_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/status/{session_id}": { + "get": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Check processing status of all attachments in a session.", + "description": "Returns each attachment's processing state (pending/processing/completed/failed) plus extracted text length and chunk count once ready. Poll this after upload_attachment_base64 to know when an attachment becomes queryable by the AI. Surfaces processing errors with actionable messages.", + "operationId": "get_attachment_status", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/upload": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Upload Document To Editor", + "description": "Upload a document file and load it into the editor.\n\nConverts the file to HTML with formatting preserved (tables, colors, images).\nImages are extracted to cloud storage and referenced by URL.\nReturns the HTML for the editor to display.\n\nAI indexing happens automatically on the first chat message.\nFor API clients who want immediate indexing, pass ?index=true.", + "operationId": "upload_document_to_editor", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "index", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Set to 'true' for immediate AI indexing (API clients). Default: conversion only.", + "title": "Index" + }, + "description": "Set to 'true' for immediate AI indexing (API clients). Default: conversion only." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_document_to_editor" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/images/upload": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Upload an image and get back a stable URL the editor can drop into .", + "description": "Upload a single inline image and return a stable URL you can reference\nin the document via .\n\nAccepts PNG/JPEG/WebP/GIF/SVG, up to 10 MB per upload. The returned URL is\npublic-read with an unguessable path. Useful for saving a drawing or\nscreenshot so a document can embed it.", + "operationId": "upload_inline_image", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_inline_image" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/images/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Upload a base64-encoded image and get back a stable URL to embed in a document via .", + "description": "Upload an image (base64-encoded) and get a stable public URL to reference\nin a document via .\n\nAccepts PNG/JPEG/WebP/GIF/SVG, up to 10 MB decoded. Send raw base64 or a\ndata: URL in image_base64. This is the agent-friendly counterpart to the\nbrowser multipart upload \u2014 use it to save a generated or fetched image so a\ndocument can embed it.", + "operationId": "upload_image_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadImageBase64Request" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/limits/increase-request": { + "post": { + "tags": [ + "v1", + "mcp", + "account" + ], + "summary": "Request higher document/chat scale limits in one call \u2014 no email needed.", + "description": "Request a higher scale limit for your account in ONE call.\n\nWHEN TO USE: after any 413 with error_code DOCUMENT_TOO_COMPLEX (a single\ndocument crossed the standard per-document page limit) or SESSION_TOO_FULL\n(the documents open in one chat crossed the standard per-chat limit). These\nare stability limits, not hard caps: the platform supports larger\ndeployments, and limits are raised per account on request.\n\nWHAT HAPPENS: the SuperDocs team is notified immediately with your account\nidentity and the numbers you send; limits are typically expanded within a\nday and you are contacted at your account email. No email writing needed \u2014\nthough hello@superdocs.app also works if you prefer.\n\nALTERNATIVES while you wait: split the file into smaller documents, upload\nit as an attachment (reference/search, not editing), start another session\nfor additional documents (init_session), or close documents you no longer\nneed (close_session_document).", + "operationId": "request_limit_increase", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LimitIncreaseRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/export/email-request": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Request Large Export Email", + "description": "Enqueue a large-export job \u2014 runs in the background and emails a\nsecure, time-limited download link when the export finishes. 24h SLA\npromised in the email.", + "operationId": "request_large_export_email", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LargeExportEmailRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "documents" + ], + "summary": "Upload .docx/PDF/HTML/MD/RTF as the active editable document with chunk-ID structural editing.", + "description": "Parses the file into structured HTML where every paragraph, heading, table, row, and cell has a unique chunk ID, enabling the AI to make targeted structural edits via chat (\"remove row 3 of the pricing table\" works). Tables, borders, shading, alternating row colors, fonts, and inline styling are preserved on edit and export. Also works for AI-generated content: if you've drafted an outline or partial document in your context, upload it here as the working doc, then use chat to fill in the rest. This is also the reliable way to REPLACE a session's document with exact content you already hold \u2014 the upload is a verbatim load, so it can never come back paraphrased or placeholder-shaped the way asking chat to re-type it can. When you pass a session_id the response is compact by default (metadata only \u2014 pass return_html=true for the full parsed HTML); the document is loaded into the session and you edit it via chat. For files >100KB, prefer request_upload_url (pre-signed URL flow) to avoid token bloat from base64 through the agent context. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm. Max 50 MB validated; note the hosted transport layer caps any REQUEST BODY at ~32 MB and rejects larger ones at the gateway with an HTML (not JSON) error \u2014 base64 inflates content ~33%, so inline uploads are reliable up to ~20 MB of file bytes; beyond that use request_upload_url (pre-signed flow, up to 100 MB).", + "operationId": "upload_document_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Upload a reference file (PDF/DOCX/image) for the AI to query while editing.", + "description": "Files are processed asynchronously and become AI-searchable once ready. The AI can then reference the attachment's content during chat (e.g., \"rewrite section 3 to match the style guide PDF I attached\"). Images are queryable via multimodal vision. Poll get_attachment_status to know when ready. Distinct from upload_document_base64: attachments are read-only context for the AI to reference, not the editable working document. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm. Max 50 MB validated; note the hosted transport layer caps any REQUEST BODY at ~32 MB and rejects larger ones at the gateway with an HTML (not JSON) error \u2014 base64 inflates content ~33%, so inline uploads are reliable up to ~20 MB of file bytes; beyond that use request_upload_url (pre-signed flow, up to 100 MB). Requires session_id.", + "operationId": "upload_attachment_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "Save a document template (NDA, contract, SOP, letterhead) for reuse across sessions.", + "description": "Templates persist across sessions and can be referenced by the AI when drafting new documents (e.g., \"draft an NDA using my standard template\"). Stored at user or organization scope. Ideal for boilerplate, branded letterheads, recurring document structures, or compliance-required templates. Supports the same formats as document upload: .docx, .pdf, .txt, .rtf, .md, .html, .htm. Max 50 MB validated; note the hosted transport layer caps any REQUEST BODY at ~32 MB and rejects larger ones at the gateway with an HTML (not JSON) error \u2014 base64 inflates content ~33%, so inline uploads are reliable up to ~20 MB of file bytes; beyond that use request_upload_url (pre-signed flow, up to 100 MB).", + "operationId": "upload_template_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "List your async chat jobs (in-progress, awaiting approval, completed, failed).", + "description": "Returns jobs sorted by most recent, optionally filtered by status. Use to monitor long-running AI edits started via chat_async, see which jobs are paused waiting for human approval, or audit completed work. Each job tracks the chat that started it, the changes made, and any HITL decisions logged.", + "operationId": "list_jobs", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter by job status", + "title": "Status" + }, + "description": "Filter by job status" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of jobs to return", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of jobs to return" + }, + { + "name": "compact", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs.", + "default": false, + "title": "Compact" + }, + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs/{job_id}": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "Get the status, partial results, and any pending changes for an async chat job.", + "description": "Returns the job's current state (pending, in_progress, awaiting_approval, completed, failed, or cancelled), intermediate AI responses streamed during execution, the final document HTML once complete, and any pending changes awaiting user approval (with chunk IDs and proposed HTML diffs in metadata.pending_changes). Poll this after chat_async to track progress and retrieve results. When status is awaiting_approval, call POST /v1/chat/{session_id}/approve to approve or deny each pending change. Headless/MCP clients get the same typed progress events the web SSE stream delivers via metadata.intermediate_responses (e.g. documents_changed, continue_prompt, model_fallback, proposed_change_batch); cross-session document updates from OTHER sessions are reported separately by polling GET /v1/sessions/{session_id}/doc-events.", + "operationId": "get_job", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Job Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs/{job_id}/cancel": { + "post": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "Cancel a pending or in-progress async chat job.", + "description": "Stops the AI mid-edit. Already-applied changes are preserved in the document; pending changes are discarded. Use to abort long-running operations that are no longer needed (e.g., user changed their mind, or you want to retry with different parameters or model_tier). Only jobs with status pending or processing can be cancelled. Returns the updated job details with status cancelled.", + "operationId": "cancel_job", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Job Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/jobs": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "List all async chat jobs for a specific session, most recent first.", + "description": "Returns the full job history of one document. Useful for auditing what the AI did to a document over time, or finding a specific job that's waiting for HITL approval. Same shape as list_jobs but scoped to one session.", + "operationId": "get_session_jobs", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of jobs to return", + "default": 20, + "title": "Limit" + }, + "description": "Maximum number of jobs to return" + }, + { + "name": "compact", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs.", + "default": false, + "title": "Compact" + }, + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/async": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Start a long-running or HITL-approved AI edit; returns a job_id to poll for results.", + "description": "Use instead of chat when (a) the edit is large or multi-step, (b) you need human approval on each proposed change before it applies (set approval_mode='ask_every_time'), or (c) you can't afford to block on a synchronous response. To replace the whole document with EXACT content you already hold, pass that content in document_html (a verbatim load \u2014 the AI never re-types it) or upload it via upload_document_base64; describing the content only in `message` makes the AI author it, which can drift from your text. Returns a job_id immediately. Poll get_job to track progress and retrieve the final document. Approve or deny pending changes via approve_change. Job state is durable and survives server restarts. Optional: model_tier (core/turbo/pro/max), thinking_depth (fast/balanced/deep), image_attachments for multimodal vision. RECOMMENDED for AI agents working with large documents (>20 pages): set response_mode='compact' so the job result skips the full HTML and surfaces only per-section diffs in chunk_diffs, saving thousands of tokens per poll. To read sections in compact mode, just send a natural-language chat request ('show me the pricing section') \u2014 the AI returns the content in the reply text. Always use natural language; the AI handles all internal section lookups. HITL workflow (approval_mode='ask_every_time'): 1) poll get_job until status=awaiting_approval, 2) read metadata.pending_changes for proposed edits, 3) call approve_change per change, 4) continue polling until status=completed.", + "operationId": "chat_async", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncChatRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncChatResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/stream": { + "get": { + "tags": [ + "v1", + "chat" + ], + "summary": "Stream Chat Progress", + "description": "Stream real-time progress for a chat job using Server-Sent Events (SSE).\n\nOpens an SSE connection that streams events as the AI processes your request.\nUse this with the job_id returned from POST /v1/chat/async.\n\nEvent types:\n- 'intermediate': Progress updates during processing (content, sequence, timestamp).\n- 'proposed_change_batch': The batch of document changes proposed for review, delivered as one\n event carrying changes[] (emitted when approval_mode is 'ask_every_time'). Changes are always\n delivered as a batch via this event.\n- 'document_sync': Chunk-id sync emitted before the agent runs, so changes can reference stable ids.\n- 'continue_prompt': A pause on a large edit, asking whether to continue or stop.\n- 'documents_changed': Signals that one or more documents were auto-applied (with per-document\n change counts and changed chunk ids).\n- 'model_fallback': Notice that the request automatically failed over to another model tier.\n- 'final': Processing complete. Contains the full result with AI response and document changes.\n- 'usage': Billing data emitted after 'final' (monthly_used, monthly_limit, monthly_remaining).\n- 'error': An error occurred (job failed, cancelled, or not found).\n\nAuthentication: Pass a token or api_key as a query parameter (required for EventSource which cannot set headers).", + "operationId": "stream_chat_progress_v1_chat__session_id__stream_get", + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "job_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "description": "Job ID to stream progress for", + "title": "Job Id" + }, + "description": "Job ID to stream progress for" + }, + { + "name": "last_sequence", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "SSE Last-Event-ID resume: only replay intermediate responses with sequence > this. Reconnects pass the highest sequence already processed so old events are NOT re-emitted; default 0 = full history.", + "default": 0, + "title": "Last Sequence" + }, + "description": "SSE Last-Event-ID resume: only replay intermediate responses with sequence > this. Reconnects pass the highest sequence already processed so old events are NOT re-emitted; default 0 = full history." + }, + { + "name": "token", + "in": "query", + "required": false, + "schema": { + "type": "string", + "description": "Authentication token (query parameter for EventSource compatibility)", + "title": "Token" + }, + "description": "Authentication token (query parameter for EventSource compatibility)" + }, + { + "name": "api_key", + "in": "query", + "required": false, + "schema": { + "type": "string", + "description": "API key (query parameter for EventSource compatibility)", + "title": "Api Key" + }, + "description": "API key (query parameter for EventSource compatibility)" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/approve": { + "post": { + "tags": [ + "v1", + "mcp", + "approval" + ], + "summary": "Approve or deny AI-proposed document changes one-by-one or in batch (HITL workflow).", + "description": "Used with chat_async when approval_mode='ask_every_time'. For each proposed change (with chunk_id and HTML diff), respond approved=true|false plus optional feedback for the AI to revise on. Approved changes apply atomically; denied changes are discarded; the AI may revise based on feedback in the next turn. Required for regulated workflows (legal, medical, compliance) where every AI edit must be reviewed before it touches the document. For single changes: set approved=true/false and optionally provide feedback. For batch decisions: provide a 'changes' array with per-change decisions. After approval, the job resumes processing and eventually reaches status=completed.", + "operationId": "approve_change", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApprovalRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/continue": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Resume or stop a chat turn paused by a large-edit continue prompt.", + "description": "Used with chat_async when a large edit paused to ask whether to keep going.\nPoll get_job until status=awaiting_approval AND metadata.awaiting_kind='continue_prompt';\nthen POST here with continue=true to resume (the AI picks up where it left off with a\nfresh time/step budget) or continue=false to stop (everything applied so far is kept).\nThe job then resumes/finishes and reaches status=completed. This is NOT the change-\napproval endpoint (that is approve_change) \u2014 it can only act on a continue-prompt pause.", + "operationId": "continue_chat", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContinueRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/poll/{session_id}": { + "get": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Poll Session Updates", + "description": "Long-poll for real-time job updates on a session (B2B organizations only).\n\nReturns immediately if there are recent job updates, or holds the connection\nopen up to the specified timeout. Use the 'since' parameter to avoid receiving\nduplicate updates.", + "operationId": "poll_session_updates_v1_poll__session_id__get", + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "since", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "ISO 8601 timestamp \u2014 only return updates after this time", + "title": "Since" + }, + "description": "ISO 8601 timestamp \u2014 only return updates after this time" + }, + { + "name": "timeout", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Long polling timeout in seconds (max 1800 = 30 minutes)", + "default": 1800, + "title": "Timeout" + }, + "description": "Long polling timeout in seconds (max 1800 = 30 minutes)" + }, + { + "name": "authorization", + "in": "header", + "required": true, + "schema": { + "type": "string", + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/upload": { + "post": { + "tags": [ + "v1", + "templates" + ], + "summary": "Upload User Template", + "description": "Upload a document file as a personal/organization template.\n\nThe file is processed synchronously: text extraction, HTML conversion, and content indexing.\nSupported formats: .docx, .pdf, .txt, .rtf, .md, .html, .htm\nTemplates are scoped to the uploading user or organization.", + "operationId": "upload_user_template", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_user_template" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates": { + "get": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "List all saved document templates available to the user or organization.", + "description": "Returns active (non-deleted) templates with name, format, size, and creation date metadata. Use to show the AI what reusable document structures are available for drafting new documents. Templates are scoped to the authenticated entity: users via the web app or sk_ key see only their own templates; organizations via lce_ key see theirs.", + "operationId": "list_user_templates", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/{template_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "Delete a saved document template by ID.", + "description": "Soft-deletes the template; only the owner (user or organization) can delete. Once deleted, the template no longer appears in list_user_templates and cannot be referenced by the AI for new documents. Existing documents already drafted from the template are unaffected.", + "operationId": "delete_user_template", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "template_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Template Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me": { + "get": { + "tags": [ + "users" + ], + "summary": "Get Current User Profile", + "description": "Get current user's profile information\n\nReturns user profile with subscription tier and usage limits", + "operationId": "get_current_user_profile_v1_users_me_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserProfileResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "patch": { + "tags": [ + "users" + ], + "summary": "Update User Profile", + "description": "Update current user's profile\n\nAllows updating display name, timezone, language, and preferences", + "operationId": "update_user_profile_v1_users_me_patch", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProfileRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/usage": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Usage Stats", + "description": "Get detailed usage statistics for current user\n\nReturns operation counts, token usage, and success rates by operation type", + "operationId": "get_user_usage_stats_v1_users_me_usage_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UsageStatsResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/sessions": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Sessions", + "description": "List all chat sessions for current user\n\nReturns list of sessions with last activity and message counts", + "operationId": "get_user_sessions_v1_users_me_sessions_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/UserSessionResponse" + }, + "type": "array", + "title": "Response Get User Sessions V1 Users Me Sessions Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/sessions/{session_id}": { + "delete": { + "tags": [ + "users" + ], + "summary": "Delete User Session", + "description": "Delete a specific chat session\n\nRemoves all messages for the given session ID (user must own the session)", + "operationId": "delete_user_session_v1_users_me_sessions__session_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/limits": { + "get": { + "tags": [ + "users" + ], + "summary": "Check User Limits", + "description": "Check current usage limits and remaining operations\n\nReturns real-time usage information with reset date", + "operationId": "check_user_limits_v1_users_me_limits_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/api-keys": { + "get": { + "tags": [ + "users" + ], + "summary": "List Api Keys", + "description": "List all API keys for the current user (masked).\n\nReturns key prefix and last 4 characters for identification.", + "operationId": "list_api_keys_v1_users_me_api_keys_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ApiKeyListItem" + }, + "type": "array", + "title": "Response List Api Keys V1 Users Me Api Keys Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "users" + ], + "summary": "Create Api Key", + "description": "Create a new API key for the current user.\n\nThe raw key is returned ONCE in the response. It cannot be retrieved again.", + "operationId": "create_api_key_v1_users_me_api_keys_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/api-keys/{key_id}": { + "delete": { + "tags": [ + "users" + ], + "summary": "Revoke Api Key", + "description": "Revoke (soft delete) an API key.\n\nThe key will be marked as inactive and can no longer be used for authentication.", + "operationId": "revoke_api_key_v1_users_me_api_keys__key_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "key_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Key Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/promo/redeem": { + "post": { + "tags": [ + "promo" + ], + "summary": "Redeem Promo", + "description": "Redeem a promo code and add the granted operations to the authenticated user's account.\n\nB2C only: web app login tokens and sk_ user API keys are accepted; lce_ org keys are rejected.\nRequires a verified email. Each user may redeem a given code at most once.\n\nRate-limited to 3 attempts per IP per hour. All attempts (success and failure) are\nlogged for audit.", + "operationId": "redeem_promo_v1_promo_redeem_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedeemRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedeemResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/promotions": { + "get": { + "tags": [ + "promo" + ], + "summary": "List My Promotions", + "description": "List the authenticated user's promotion redemptions, split into `active` (drawable now)\nand `history` (exhausted / expired / revoked).\n\nUsed by the Settings \u2192 Billing tab to render the Credits & Promotions section.", + "operationId": "list_my_promotions_v1_users_me_promotions_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPromotionsResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/uploads": { + "post": { + "tags": [ + "uploads", + "mcp", + "uploads" + ], + "summary": "Get a pre-signed URL to upload large files (.docx/PDF/HTML/MD/RTF) without bloating agent context.", + "description": "Returns a short-lived (5-minute) PUT URL plus a ready-to-run `curl_example`. An agent with shell access can run the `curl_example` directly to push the file to cloud storage, so the bytes never pass through its context window; clients without shell execution can surface the command or URL for the caller to run. After upload completes, call process_uploaded_document with the upload_id to trigger parsing. For files <100KB where token cost is trivial, upload_document_base64 still works inline. Max file size: 100 MB. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm, .tex (plus .zip LaTeX project archives).", + "operationId": "request_upload_url", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadUrlRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/uploads/{upload_id}/process": { + "post": { + "tags": [ + "uploads", + "mcp", + "uploads" + ], + "summary": "Parse an uploaded file into structured HTML with chunk IDs for targeted AI editing.", + "description": "Fetches the file uploaded via request_upload_url and runs the same parsing pipeline as upload_document_base64: every paragraph, heading, table, row, and cell gets a unique chunk ID, enabling targeted structural edits via chat (\"remove row 3 of the pricing table\" works), with tables, borders, shading, alternating row colors, fonts, and inline styling preserved on edit and export. By default the response is compact (metadata only, no document body) to keep your context small \u2014 the document is loaded into the session and you edit it via chat; pass return_html=true if you need the parsed HTML inline. Uploading and parsing is NOT itself a billable operation \u2014 you are charged only when the AI edits the document. Specify parse_mode='document' to load as the active editable document, or parse_mode='attachment' to load as a read-only AI-searchable reference.", + "operationId": "process_uploaded_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "upload_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The upload_id returned by request_upload_url.", + "title": "Upload Id" + }, + "description": "The upload_id returned by request_upload_url." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessUploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessDocumentResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/downloads": { + "post": { + "tags": [ + "uploads", + "mcp", + "downloads" + ], + "summary": "Get a pre-signed URL to download an exported document without proxying through the agent.", + "description": "Returns a short-lived (15-minute) GET URL plus a ready-to-run `curl_example`. An agent with shell access can run the `curl_example` directly to save the exported file to the working directory, so the bytes never pass through its context window; clients without shell execution (or callers who only want the link) can use the signed URL directly. Generates the document in the requested format and returns a time-limited signed download URL. Specify format as 'pdf', 'docx', 'html', 'markdown', or 'txt' (legacy 'doc' also accepted).", + "operationId": "request_download_url", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadUrlRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/challenge": { + "get": { + "tags": [ + "agents" + ], + "summary": "Agent Challenge", + "description": "Get a proof-of-work challenge for signup (only needed when the challenge gate is enabled).\n\nSolve it headlessly with no browser: brute-force an integer `n` in\n[0, maxNumber] such that `SHA-256(salt + str(n))` equals `challenge`, then\nbase64-encode `{algorithm, challenge, number:n, salt, signature}` and send it\nas the `altcha` field in POST /v1/agents/signup.", + "operationId": "agent_challenge_v1_agents_challenge_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/v1/agents/signup": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Signup", + "description": "Create a SuperDocs account and get a working API key in one call \u2014 no human required.\n\nYou (an AI agent) get a normal **free account: 500 operations/month**, exactly\nlike a human. Use the returned `sk_` key as `Authorization: Bearer ` on\nthis API or the MCP server at `/mcp`. Store the key \u2014 it is shown only once.\nSave the whole response to `~/.superdocs/agent_credentials.json` and call\n`GET /v1/agents/whoami` before signing up again so you reuse the same account.\nWhen you approach the 500-op cap, hand the account to your human\n(POST /v1/agents/handoff) so they can adopt it and pay to continue. If you\nare acting for a user, confirm with them before accepting the terms.", + "operationId": "agent_signup_v1_agents_signup_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentSignupRequest" + }, + { + "type": "null" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentSignupResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/whoami": { + "get": { + "tags": [ + "agents", + "mcp" + ], + "summary": "Check your account status: subscription tier plus monthly operations used and remaining.", + "description": "Check your SuperDocs account status: tier, monthly operations used/remaining, and (for agent accounts) whether a human has adopted it.\n\nCall this with your `sk_` key. Useful before doing work (to confirm you have\noperations left) and before signing up again (to reuse your account).\n\nNearing your monthly cap and need to keep going? Hand the account to a human:\nPOST /v1/agents/handoff with their email returns a one-time takeover code to\ngive them; they open the emailed link, enter the code, and adopt the account\nin place (your work and this API key keep working) so they can upgrade and\npay. No human? POST /v1/agents/request-upgrade instead. Full flow:\nhttps://docs.superdocs.app/introduction/agent-signup", + "operationId": "get_account_status", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentWhoamiResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/handoff": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Handoff", + "description": "Email your human a one-time link to ADOPT this account (take ownership + pay).\n\nCall with your `sk_` key. Pass `working_context` (e.g. \"the ~/Documents/acme\nproject on your Mac\") so the email is recognizable and not mistaken for spam.\n\nThe response includes a short **takeover_code** (like ABCD-1234). You MUST show\nthis code to your human operator \u2014 they enter it after opening the emailed link\nand signing in. It is the security check that stops anyone who merely received\nthe email (a wrong address, or a prompt-injected one) from taking the account.\nTell your operator the code directly (you may also save it locally, e.g.\n~/.superdocs/takeover-code.txt); never email it or post it anywhere external.\n\nThe human opens the link, signs in, enters the code, and the account becomes\ntheirs in place \u2014 you keep all your work and this API key keeps working.", + "operationId": "agent_handoff_v1_agents_handoff_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentHandoffRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/adopt": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Adopt", + "description": "Human takes over an agent account IN PLACE. Auth = the human's signed-in identity token.\n\nCalled by the web app's adopt page after the human signs in. Verifies the\nhuman's signed-in identity (WITHOUT the get-or-create middleware, to avoid\nminting a duplicate row), then re-points the agent account row to that\nidentity \u2014 `users.id` is preserved, so the agent's documents, usage, and API\nkey all stay. If the human already has an account, returns 409.", + "operationId": "agent_adopt_v1_agents_adopt_post", + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentAdoptRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/adopt-info": { + "get": { + "tags": [ + "agents" + ], + "summary": "Agent Adopt Info", + "description": "Public, token-keyed context for the adopt page: WHICH agent set this account up,\nwhere it runs, and where to find the takeover code. Recognition / anti-phishing only.\nNEVER returns the code itself; the unguessable token is the gate.", + "operationId": "agent_adopt_info_v1_agents_adopt_info_get", + "parameters": [ + { + "name": "token", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Token" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/request-upgrade": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Request Upgrade", + "description": "For a FULLY AUTONOMOUS agent with no human who can upgrade: register interest in paying directly.\n\nWe don't offer agent-native (no-human) payment yet, but we're gauging demand.\nCall this with your `sk_` key when you've hit your cap and genuinely cannot\ninvolve a human. We record it and follow up (or reach hello@superdocs.app).\nIf a human CAN help, use POST /v1/agents/handoff instead \u2014 that upgrades you now.", + "operationId": "agent_request_upgrade_v1_agents_request_upgrade_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentUpgradeRequest" + }, + { + "type": "null" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/": { + "get": { + "summary": "Root", + "operationId": "root__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health": { + "get": { + "tags": [ + "mcp" + ], + "summary": "Verify the SuperDocs MCP server is reachable and serving traffic.", + "description": "Returns 200 with {\"status\":\"healthy\"} when the API is up. Call this once after MCP install to confirm the connection works before invoking other tools. No authentication required.", + "operationId": "health", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health/deep": { + "get": { + "summary": "Health Check Deep", + "description": "Deep health check that verifies the job queue, AI workflow state store, and database are all responsive within a bounded time budget. Used by container orchestrators as a liveness probe to distinguish \"process alive and TCP listening\" (what /health reports) from \"application is actually serving requests\" (this endpoint).", + "operationId": "health_check_deep_health_deep_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/oauth-protected-resource": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource_mcp__well_known_oauth_protected_resource_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-protected-resource/mcp": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource__well_known_oauth_protected_resource_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-protected-resource": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource__well_known_oauth_protected_resource_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/oauth-authorization-server": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server_mcp__well_known_oauth_authorization_server_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-authorization-server/mcp": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server__well_known_oauth_authorization_server_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-authorization-server": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server__well_known_oauth_authorization_server_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/openid-configuration": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration_mcp__well_known_openid_configuration_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/openid-configuration/mcp": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration__well_known_openid_configuration_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/openid-configuration": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration__well_known_openid_configuration_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/mcp.json": { + "get": { + "summary": "Mcp Server Card", + "operationId": "mcp_server_card__well_known_mcp_json_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/mcp-server-card": { + "get": { + "summary": "Mcp Server Card", + "operationId": "mcp_server_card__well_known_mcp_server_card_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/api-catalog": { + "get": { + "summary": "Api Catalog", + "operationId": "api_catalog__well_known_api_catalog_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ActivePromotionInfo": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id", + "description": "Unique identifier for this promotion grant." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Human-friendly promotion name (e.g., 'YC SUS India 2026 cohort')." + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining", + "description": "Operations still available in this promotion bucket." + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted", + "description": "Total operations originally granted by this redemption." + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At", + "description": "ISO 8601 expiry timestamp. Null if the credits never expire." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_remaining", + "ops_granted" + ], + "title": "ActivePromotionInfo", + "description": "A single active (drawable) promotion belonging to the authenticated user." + }, + "ActivePromotionOut": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + } + }, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at" + ], + "title": "ActivePromotionOut", + "description": "A single active (drawable) promotion grant belonging to the current user." + }, + "AgentAdoptRequest": { + "properties": { + "token": { + "type": "string", + "maxLength": 128, + "title": "Token", + "description": "The handoff token from the adopt link." + }, + "code": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Code", + "description": "The one-time takeover code your AI agent shows you (8 characters like ABCD-1234). Required for accounts handed off with a code." + } + }, + "type": "object", + "required": [ + "token" + ], + "title": "AgentAdoptRequest" + }, + "AgentHandoffRequest": { + "properties": { + "email": { + "type": "string", + "maxLength": 255, + "title": "Email", + "description": "Your human operator's email. A one-time link to adopt this account is sent there." + }, + "working_context": { + "anyOf": [ + { + "type": "string", + "maxLength": 300 + }, + { + "type": "null" + } + ], + "title": "Working Context", + "description": "Recommended: a short, human-readable description of where you are running, so your operator recognizes the email and it isn't mistaken for spam (e.g. 'the ~/Documents/acme project on your Mac', or 'the Acme Slack workspace')." + }, + "code_location": { + "anyOf": [ + { + "type": "string", + "maxLength": 300 + }, + { + "type": "null" + } + ], + "title": "Code Location", + "description": "Optional: where you show or save the takeover code so your operator can find it (e.g. 'this chat', or '~/.superdocs/takeover-code.txt')." + } + }, + "type": "object", + "required": [ + "email" + ], + "title": "AgentHandoffRequest" + }, + "AgentQuota": { + "properties": { + "tier": { + "type": "string", + "title": "Tier" + }, + "monthly_limit": { + "type": "integer", + "title": "Monthly Limit" + }, + "used": { + "type": "integer", + "title": "Used" + }, + "remaining": { + "type": "integer", + "title": "Remaining" + }, + "resets_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Resets At" + } + }, + "type": "object", + "required": [ + "tier", + "monthly_limit", + "used", + "remaining" + ], + "title": "AgentQuota" + }, + "AgentSignupRequest": { + "properties": { + "terms_accepted": { + "type": "boolean", + "title": "Terms Accepted", + "description": "Must be true. Accepts the Terms at https://superdocs.app/terms. If you are an agent, confirm with your user first.", + "default": false + }, + "agent_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Agent Name", + "description": "Optional label for this agent account." + }, + "operated_by_email": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Operated By Email", + "description": "Optional. Your human operator's email (only used if you later hand off the account)." + }, + "model_metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Model Metadata", + "description": "Optional. Free-form metadata about the agent/model (forensics; capped at 10KB)." + }, + "altcha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Altcha", + "description": "Only required if the proof-of-work challenge is enabled: the base64 solution from GET /v1/agents/challenge." + } + }, + "type": "object", + "title": "AgentSignupRequest", + "description": "Body for POST /v1/agents/signup. Every field except terms_accepted is optional." + }, + "AgentSignupResponse": { + "properties": { + "account_id": { + "type": "string", + "title": "Account Id" + }, + "slug": { + "type": "string", + "title": "Slug" + }, + "email": { + "type": "string", + "title": "Email" + }, + "api_key": { + "type": "string", + "title": "Api Key", + "description": "Your API key (sk_...). Shown ONCE. Send it as `Authorization: Bearer `. Store it now." + }, + "quota": { + "$ref": "#/components/schemas/AgentQuota" + }, + "endpoints": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Endpoints" + }, + "mcp_setup": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Mcp Setup", + "description": "Copy-paste commands to connect the SuperDocs MCP server to your client, plus the REST fallback." + }, + "handoff": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Handoff" + }, + "important": { + "type": "string", + "title": "Important" + } + }, + "type": "object", + "required": [ + "account_id", + "slug", + "email", + "api_key", + "quota", + "endpoints", + "mcp_setup", + "handoff", + "important" + ], + "title": "AgentSignupResponse" + }, + "AgentUpgradeRequest": { + "properties": { + "note": { + "anyOf": [ + { + "type": "string", + "maxLength": 1000 + }, + { + "type": "null" + } + ], + "title": "Note", + "description": "Optional: your use case, or why you can't involve a human." + } + }, + "type": "object", + "title": "AgentUpgradeRequest" + }, + "AgentWhoamiResponse": { + "properties": { + "account_id": { + "type": "string", + "title": "Account Id" + }, + "tier": { + "type": "string", + "title": "Tier" + }, + "quota": { + "$ref": "#/components/schemas/AgentQuota" + }, + "is_agent_account": { + "type": "boolean", + "title": "Is Agent Account" + }, + "adopted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Adopted", + "description": "For agent accounts: whether a human has adopted it yet. Null for regular accounts." + }, + "hint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Hint" + } + }, + "type": "object", + "required": [ + "account_id", + "tier", + "quota", + "is_agent_account" + ], + "title": "AgentWhoamiResponse" + }, + "ApiKeyListItem": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Key identifier (UUID)." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Label assigned to this key." + }, + "key_prefix": { + "type": "string", + "title": "Key Prefix", + "description": "First 7 characters of the key (e.g., 'sk_a1b2')." + }, + "last_four": { + "type": "string", + "title": "Last Four", + "description": "Last 4 characters of the key for identification." + }, + "is_active": { + "type": "boolean", + "title": "Is Active", + "description": "Whether the key is active. Revoked keys show as false." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the key was created." + }, + "last_used_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Used At", + "description": "ISO 8601 timestamp of last use, or null if never used." + } + }, + "type": "object", + "required": [ + "id", + "name", + "key_prefix", + "last_four", + "is_active", + "created_at" + ], + "title": "ApiKeyListItem", + "description": "API key summary (masked for security)." + }, + "ApprovalRequest": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job ID that is awaiting approval." + }, + "change_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Change Id", + "description": "Specific change to approve/deny (for individual decisions). Get change IDs from the job's pending_changes." + }, + "approved": { + "type": "boolean", + "title": "Approved", + "description": "Whether to approve (true) or deny (false) the change." + }, + "feedback": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Feedback", + "description": "Optional feedback explaining why a change was denied. Helps the AI adjust future suggestions." + }, + "changes": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Changes", + "description": "Batch decisions: array of {change_id, approved, feedback} objects. Use for 'Accept All' or 'Deny All'." + } + }, + "type": "object", + "required": [ + "job_id", + "approved" + ], + "title": "ApprovalRequest", + "description": "Approve or deny proposed AI document changes." + }, + "AsyncChatRequest": { + "properties": { + "message": { + "type": "string", + "maxLength": 100000, + "title": "Message", + "description": "Your message to the AI assistant." + }, + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Unique session identifier. Reuse to continue a conversation." + }, + "document_html": { + "anyOf": [ + { + "type": "string", + "maxLength": 50000000 + }, + { + "type": "null" + } + ], + "title": "Document Html", + "description": "Current document HTML. OMIT this when the session already holds the document \u2014 the server persists it across turns, so you don't re-send it every turn; pass it only to load new content or replace the document wholesale (a verbatim load \u2014 the AI never re-types content passed here). Include data-chunk-id attributes on elements to enable targeted AI edits." + }, + "user_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User identifier. Automatically set from authentication \u2014 typically omit this." + }, + "image_attachments": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ImageAttachmentData" + }, + "type": "array", + "maxItems": 20 + }, + { + "type": "null" + } + ], + "title": "Image Attachments", + "description": "Inline images for vision-based analysis (base64-encoded)." + }, + "async_mode": { + "type": "boolean", + "title": "Async Mode", + "description": "Must be true for async processing. Included for backward compatibility.", + "default": true + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier", + "description": "AI model to use: 'core' (default, fast), 'turbo' (fastest), 'pro' (advanced reasoning), 'max' (most capable)." + }, + "thinking_depth": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Thinking Depth", + "description": "Reasoning depth: 'fast', 'balanced' (default), or 'deep'. Controls how much analysis the AI performs." + }, + "approval_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Approval Mode", + "description": "Change review mode: 'approve_all' (default, auto-applies changes) or 'ask_every_time' (pauses for your review)." + }, + "response_mode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "full", + "compact" + ] + }, + { + "type": "null" + } + ], + "title": "Response Mode", + "description": "Response shape control. 'full' (default) puts the complete updated document HTML in the job result \u2014 required by web app editors and recommended for small documents. 'compact' (recommended for AI agents editing large documents) suppresses the full HTML and stores only per-section diffs (chunk_diffs), saving thousands of tokens when polling get_job. To read sections in compact mode, send a natural-language request to chat ('show me the pricing section') \u2014 the AI returns the content in the reply text.", + "default": "full" + }, + "cursor_context": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Cursor Context", + "description": "Editor cursor context at message time, used to default the insert location for new images, diagrams, or sections when the user's prompt doesn't name a position. Shape: {chunk_id: str, pos_in_chunk?: int, text_before?: str, text_after?: str}. Omit when the user typed in chat without focusing the editor first." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Target a specific open document by id (ids come from /v1/sessions/{session_id}/documents). Omit to target the focused document (default \u2014 unchanged single-document behaviour). When set, that document becomes the focus for this turn." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window (set by the web app) so concurrent edits from two windows of the same chat are merged rather than overwritten. Agents/API may omit." + }, + "cross_session_memory": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory", + "description": "When true, the AI carries a private rolling memory of context across THIS account's chats (off by default). Per-request override of your saved Settings default." + }, + "cross_session_search": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Search", + "description": "When true, the AI may search your PAST chats and documents on demand to reuse prior work and open a found document (off by default). Per-request override of your saved Settings default; never affects the always-on search of the open document." + }, + "cross_session_memory_key": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory Key", + "description": "Optional per-request id of YOUR end-customer so the cross-session memory note is kept in a separate file per end-customer. Omit for one account-level note (the default / B2C)." + }, + "cross_session_scope": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 2000 + }, + { + "type": "null" + } + ], + "title": "Cross Session Scope", + "description": "Optional list of session ids to restrict cross-session SEARCH to (e.g. one end-customer's sessions). Omit to search all of this account's sessions. Only narrows within the API-key owner; ignored unless cross_session_search is on." + }, + "touched_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Touched Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) the user actually edited since the last sync, sent alongside document_html. When present, a chunk NOT in this list whose text is unchanged keeps its stored formatting byte-for-byte even if the client serialized it differently (protects styling from editor round-trip loss). Chunks in the list \u2014 and any chunk whose text changed \u2014 always take the submitted content. Omit for the default behavior (any differing chunk is treated as an edit)." + }, + "deleted_part_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Deleted Part Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) of OUT-OF-FLOW part sections (page headers/footers, footnote/endnote bodies, comments) to delete explicitly, sent alongside document_html. Out-of-flow parts absent from document_html are always KEPT \u2014 an editor view not containing them is their normal state, never a deletion \u2014 so removing one requires naming its id here. Ids that are not stored out-of-flow parts are ignored (in-flow content keeps the default behavior). Omit when deleting nothing." + } + }, + "type": "object", + "required": [ + "message", + "session_id" + ], + "title": "AsyncChatRequest", + "description": "Start an async chat request that processes in the background." + }, + "AsyncChatResponse": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job identifier. Poll GET /v1/jobs/{job_id} for status and results." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier for this conversation." + }, + "status": { + "type": "string", + "title": "Status", + "description": "Initial job status (always 'pending')." + }, + "message": { + "type": "string", + "title": "Message", + "description": "Human-readable status message.", + "default": "Chat request queued for processing" + } + }, + "type": "object", + "required": [ + "job_id", + "session_id", + "status" + ], + "title": "AsyncChatResponse", + "description": "Response from starting an async chat request." + }, + "Base64UploadRequest": { + "properties": { + "filename": { + "type": "string", + "maxLength": 512, + "title": "Filename", + "description": "Original filename with extension (e.g. 'contract.pdf'). Used for file type detection." + }, + "file_base64": { + "type": "string", + "maxLength": 50000000, + "title": "File Base64", + "description": "Base64-encoded file content." + }, + "session_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session ID. Auto-generated if not provided." + }, + "return_html": { + "type": "boolean", + "title": "Return Html", + "description": "With a session_id: false (default) returns compact metadata only (chunks_count, version_id, page_setup) \u2014 the document is loaded into the session and you edit it via chat, keeping your context small; true returns the full parsed HTML inline. Ignored on the convert-only path (no session_id), which always returns the converted html.", + "default": false + } + }, + "type": "object", + "required": [ + "filename", + "file_base64" + ], + "title": "Base64UploadRequest", + "description": "JSON request body for base64-encoded file uploads." + }, + "Body_upload_attachment": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id" + }, + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "session_id", + "file" + ], + "title": "Body_upload_attachment" + }, + "Body_upload_document_to_editor": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id" + }, + "open_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Open Mode", + "default": "replace" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_document_to_editor" + }, + "Body_upload_inline_image": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_inline_image" + }, + "Body_upload_user_template": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_user_template" + }, + "ChatResponse": { + "properties": { + "response": { + "type": "string", + "title": "Response", + "description": "AI assistant's response text." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier for this conversation." + }, + "document_changes": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentChanges" + }, + { + "type": "null" + } + ], + "description": "Document modifications made by the AI. Present only when the document was changed." + }, + "usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/UsageInfo" + }, + { + "type": "null" + } + ], + "description": "Operation usage data. Present for authenticated users with usage tracking." + }, + "hint": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Hint", + "description": "Advisory guidance for API callers (additive; absent on most responses). Currently: prefer_chat_async_for_large_generations \u2014 this turn ran long enough that the same work submitted via POST /v1/chat/async would be safer (no gateway timeout risk) and reports progress." + } + }, + "type": "object", + "required": [ + "response", + "session_id" + ], + "title": "ChatResponse", + "description": "AI response with optional document changes and usage data.", + "examples": [ + { + "document_changes": { + "changes_summary": "Document updated by AI", + "updated_html": "

Section 3

Updated content...

", + "version_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7" + }, + "response": "I've added a confidentiality clause to section 3 covering non-disclosure obligations.", + "session_id": "session_abc123", + "usage": { + "monthly_limit": 500, + "monthly_remaining": 458, + "monthly_used": 42, + "subscription_tier": "free", + "was_billable": true + } + } + ] + }, + "ContinueRequest": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job ID awaiting a continue decision." + }, + "continue": { + "type": "boolean", + "title": "Continue", + "description": "True to resume with a fresh budget; false to stop here (work so far is kept)." + } + }, + "type": "object", + "required": [ + "job_id", + "continue" + ], + "title": "ContinueRequest", + "description": "Resume or stop a chat turn that paused to ask whether to continue." + }, + "CreateApiKeyRequest": { + "properties": { + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1, + "title": "Name", + "description": "A label for this key (e.g., 'My App', 'CI Pipeline')." + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "CreateApiKeyRequest", + "description": "Create a new API key." + }, + "CreateApiKeyResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Key identifier (UUID). Use this to revoke the key later." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Label you assigned to this key." + }, + "key": { + "type": "string", + "title": "Key", + "description": "The full API key (sk_... format). Copy this now \u2014 it cannot be retrieved again." + }, + "key_prefix": { + "type": "string", + "title": "Key Prefix", + "description": "First 7 characters of the key (e.g., 'sk_a1b2') for identification." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the key was created." + } + }, + "type": "object", + "required": [ + "id", + "name", + "key", + "key_prefix", + "created_at" + ], + "title": "CreateApiKeyResponse", + "description": "Newly created API key. The raw key is shown only once \u2014 save it immediately." + }, + "DocumentChanges": { + "properties": { + "updated_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Updated Html", + "description": "Full updated document HTML with data-chunk-id attributes. Apply this to your editor to sync the document. Present in 'full' response mode (default); null in 'compact' mode." + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id", + "description": "Document version identifier. Changes on every modification." + }, + "changes_summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Changes Summary", + "description": "Human-readable summary of what was changed." + }, + "requires_approval": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Requires Approval", + "description": "If true, changes need review via the approve endpoint before being applied." + }, + "pending_changes": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pending Changes", + "description": "Proposed changes awaiting approval. Present when requires_approval is true." + }, + "changes": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Changes", + "description": "History of individual changes applied during this request." + }, + "chunk_diffs": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Chunk Diffs", + "description": "Per-section before/after content for changes that were applied this turn. Present in 'compact' response mode (when updated_html is null) so the agent can verify what was modified without paying token cost for the full document. Same shape as pending_changes." + } + }, + "additionalProperties": true, + "type": "object", + "title": "DocumentChanges", + "description": "Document modifications produced by the AI assistant." + }, + "DocumentState": { + "properties": { + "html_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Html Content", + "description": "Full document HTML content with data-chunk-id attributes." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Session-local id (slug) of the focused document. Identifies the document on restore so a restored tab stays in sync with edits made to it in other sessions." + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id", + "description": "Document version identifier (UUID string)." + }, + "chunk_count": { + "type": "integer", + "title": "Chunk Count", + "description": "Number of content sections in the document.", + "default": 0 + }, + "last_modified": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Modified", + "description": "ISO 8601 timestamp of the last modification." + }, + "attachments": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Attachments", + "description": "Session attachments. Each item has: id (string), filename (string), file_extension (string), processing_status ('ready', 'processing', or 'failed')." + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup", + "description": "Source page geometry of the focused document (width_in, height_in, margin_in{top,right,bottom,left}, orientation) when known, else null. The editor renders the page at this geometry on restore; null \u21d2 US-Letter + 1in default." + } + }, + "type": "object", + "title": "DocumentState", + "description": "Current document state for session restoration." + }, + "DownloadUrlRequest": { + "properties": { + "session_id": { + "type": "string", + "maxLength": 256, + "title": "Session Id", + "description": "Session ID whose current document should be exported and packaged for download." + }, + "format": { + "type": "string", + "enum": [ + "docx", + "pdf", + "html", + "markdown", + "txt", + "doc" + ], + "title": "Format", + "description": "Export format (docx, pdf, html, markdown, txt).", + "default": "docx" + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Optional Content-Disposition filename for the download (without extension). Auto-detected from the document's first heading if not provided." + }, + "options": { + "$ref": "#/components/schemas/ExportOptions", + "description": "Customization (paper, margins, filename, watermark, etc.). Mirrors ExportRequest.options." + } + }, + "type": "object", + "required": [ + "session_id" + ], + "title": "DownloadUrlRequest" + }, + "DownloadUrlResponse": { + "properties": { + "download_url": { + "type": "string", + "title": "Download Url" + }, + "expires_at": { + "type": "string", + "title": "Expires At" + }, + "expires_in_seconds": { + "type": "integer", + "title": "Expires In Seconds" + }, + "curl_example": { + "type": "string", + "title": "Curl Example" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "format": { + "type": "string", + "title": "Format" + } + }, + "type": "object", + "required": [ + "download_url", + "expires_at", + "expires_in_seconds", + "curl_example", + "filename", + "format" + ], + "title": "DownloadUrlResponse" + }, + "EnhancedChatHistoryResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier." + }, + "messages": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Messages", + "description": "Ordered list of messages. Each item has: id (string), sender ('user' or 'ai'), content (string), timestamp (ISO 8601), turn_index (int), checkpoint_id (string or null \u2014 present on turns recorded after the revertability feature shipped; null for legacy turns where revert is unavailable)." + }, + "document_state": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentState" + }, + { + "type": "null" + } + ], + "description": "Current document state. Present if the session has an active document." + }, + "editor_action": { + "type": "string", + "title": "Editor Action", + "description": "Client instruction: 'update' (load document_state HTML into editor), 'clear' (reset editor), or 'keep' (no change).", + "default": "keep" + } + }, + "type": "object", + "required": [ + "session_id", + "messages" + ], + "title": "EnhancedChatHistoryResponse", + "description": "Conversation history with document state and restoration instructions." + }, + "ExportOptions": { + "properties": { + "paper_size": { + "type": "string", + "enum": [ + "A4", + "Letter", + "A3", + "Legal" + ], + "title": "Paper Size", + "description": "Page size for DOCX/PDF/legacy .doc. HTML/MD/TXT exports ignore.", + "default": "Letter" + }, + "orientation": { + "type": "string", + "enum": [ + "portrait", + "landscape" + ], + "title": "Orientation", + "description": "Page orientation for DOCX/PDF/legacy .doc.", + "default": "portrait" + }, + "margins": { + "type": "string", + "enum": [ + "narrow", + "normal", + "wide", + "custom" + ], + "title": "Margins", + "description": "Page margins preset. 'narrow' = 0.5in, 'normal' = 1.0in, 'wide' = 1.5in. 'custom' uses custom_margins_inches.", + "default": "normal" + }, + "custom_margins_inches": { + "anyOf": [ + { + "additionalProperties": { + "type": "number" + }, + "propertyNames": { + "enum": [ + "top", + "right", + "bottom", + "left" + ] + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Margins Inches", + "description": "Required when margins='custom'. Each value 0.25-3.0 inches." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Custom filename (no extension). Auto-detected from first

if unset." + }, + "embed_images": { + "type": "boolean", + "title": "Embed Images", + "description": "HTML export only. When True, images are base64-embedded for offline portability (raises size cap to 150 MB). When False, images are referenced by URL (smaller file, online-only).", + "default": false + }, + "watermark_text": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Watermark Text", + "description": "PDF only. Optional text watermark overlaid on every page." + }, + "watermark_opacity": { + "type": "number", + "maximum": 1.0, + "minimum": 0.05, + "title": "Watermark Opacity", + "description": "PDF watermark opacity, 0.05-1.0.", + "default": 0.3 + }, + "fidelity": { + "anyOf": [ + { + "type": "string", + "enum": [ + "strict", + "compat" + ] + }, + { + "type": "null" + } + ], + "title": "Fidelity", + "description": "Export fidelity. 'strict' (server default) renders only the formatting the document carries \u2014 no imposed table widths/borders (except visual defaults on fully unstyled tables), overflow-only clamping, highlight colors honored. 'compat' reproduces the legacy normalized output." + } + }, + "type": "object", + "title": "ExportOptions", + "description": "User-facing export customisation (page size, orientation, margins,\nfilename, watermark). Sent on every export request." + }, + "ExportRequest": { + "properties": { + "session_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session ID to export from (the document is taken from the session). Required if html is omitted." + }, + "html": { + "anyOf": [ + { + "type": "string", + "maxLength": 200000000 + }, + { + "type": "null" + } + ], + "title": "Html", + "description": "HTML content to export inline. Used instead of the session document if provided." + }, + "format": { + "type": "string", + "enum": [ + "docx", + "pdf", + "html", + "markdown", + "txt", + "doc" + ], + "title": "Format", + "description": "Export format. One of docx (default), pdf, html, markdown, txt.", + "default": "docx" + }, + "options": { + "$ref": "#/components/schemas/ExportOptions", + "description": "Customization (paper, margins, filename, watermark, etc.)." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "DEPRECATED. Use options.filename. Top-level kept for legacy callers." + }, + "source_filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Source Filename", + "description": "The document's original/source filename (e.g. the name it was uploaded under). Used as the default export filename when no explicit options.filename is set, taking precedence over the first heading." + }, + "upload_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Upload Id", + "description": "ID returned by /v1/uploads (large-document upload flow, for payloads above ~25 MB). When set, the route fetches the HTML body from the uploaded payload instead of using the html field." + } + }, + "type": "object", + "title": "ExportRequest", + "description": "Unified request body for POST /v1/documents/export.\n\nBackward-compat notes:\n- Top-level ``filename`` is retained for legacy clients that sent only\n ``{ html }`` and relied on the filename being auto-extracted from the\n first

. New clients should use ``options.filename`` \u2014\n options.filename wins when both are set.\n- ``format`` defaults to \"docx\". Legacy callers passing format=\"doc\"\n explicitly keep working during a short back-compat window." + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "HistoricalPromotionOut": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + }, + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at", + "status" + ], + "title": "HistoricalPromotionOut", + "description": "An exhausted, expired, or revoked promotion grant kept for the user's history view." + }, + "ImageAttachmentData": { + "properties": { + "id": { + "type": "string", + "maxLength": 256, + "title": "Id", + "description": "Unique identifier for this image." + }, + "name": { + "type": "string", + "maxLength": 512, + "title": "Name", + "description": "Original filename of the image." + }, + "base64Data": { + "type": "string", + "maxLength": 50000000, + "title": "Base64Data", + "description": "Base64-encoded image data." + }, + "mimeType": { + "type": "string", + "maxLength": 128, + "title": "Mimetype", + "description": "MIME type (e.g., 'image/png', 'image/jpeg')." + }, + "size": { + "type": "integer", + "maximum": 50000000.0, + "title": "Size", + "description": "File size in bytes." + } + }, + "type": "object", + "required": [ + "id", + "name", + "base64Data", + "mimeType", + "size" + ], + "title": "ImageAttachmentData", + "description": "Inline image attachment for vision-based analysis." + }, + "InitSessionRequest": { + "properties": { + "document_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Document Ids", + "description": "Durable document ids (from GET /v1/documents) to open into the brand-new session. Omit/empty to start an empty session." + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Optional client-chosen session id (the web UI passes its own so its session model stays consistent). Omit and the server mints one \u2014 the one-call way for an MCP/API integrator to start a session with documents already open." + } + }, + "type": "object", + "title": "InitSessionRequest" + }, + "JobListResponse": { + "properties": { + "jobs": { + "items": { + "$ref": "#/components/schemas/JobResponse" + }, + "type": "array", + "title": "Jobs", + "description": "Array of job details." + }, + "total": { + "type": "integer", + "title": "Total", + "description": "Total number of jobs returned." + } + }, + "type": "object", + "required": [ + "jobs", + "total" + ], + "title": "JobListResponse", + "description": "List of async jobs." + }, + "JobMetadata": { + "properties": { + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Uploaded filename (attachment processing jobs)." + }, + "file_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "File Size", + "description": "File size in bytes (attachment processing jobs)." + }, + "content_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content Type", + "description": "MIME type of uploaded file (attachment processing jobs)." + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Message", + "description": "Original user message (chat jobs)." + }, + "document_html_provided": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Document Html Provided", + "description": "Whether document HTML was included in the request (chat jobs)." + }, + "pending_changes": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pending Changes", + "description": "Proposed changes awaiting approval. Present when job status is 'awaiting_approval'." + }, + "intermediate_responses": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Intermediate Responses", + "description": "Progress updates during processing. Each item has: type, content, sequence, timestamp." + } + }, + "additionalProperties": true, + "type": "object", + "title": "JobMetadata", + "description": "Job metadata. Contents vary by job type." + }, + "JobResponse": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Unique job identifier. Use this to poll for status updates." + }, + "organization_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Organization Id", + "description": "Organization that owns this job (null for user jobs)." + }, + "user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User that owns this job (null for organization jobs)." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session associated with this job." + }, + "job_type": { + "type": "string", + "title": "Job Type", + "description": "Type of job: 'chat' or 'attachment_processing'." + }, + "status": { + "type": "string", + "title": "Status", + "description": "Current status: 'pending', 'in_progress', 'awaiting_approval', 'completed', 'failed', or 'cancelled'." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the job was created." + }, + "updated_at": { + "type": "string", + "title": "Updated At", + "description": "ISO 8601 timestamp of the last status change." + }, + "progress": { + "type": "integer", + "title": "Progress", + "description": "Progress percentage (0-100)." + }, + "result": { + "anyOf": [ + { + "$ref": "#/components/schemas/JobResult" + }, + { + "type": "null" + } + ], + "description": "Job output. Present when status is 'completed'. Contains AI response and document changes for chat jobs." + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error", + "description": "Error message. Present when status is 'failed'." + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/JobMetadata" + }, + { + "type": "null" + } + ], + "description": "Job metadata and context. Contains pending_changes when status is 'awaiting_approval'." + } + }, + "type": "object", + "required": [ + "job_id", + "session_id", + "job_type", + "status", + "created_at", + "updated_at", + "progress" + ], + "title": "JobResponse", + "description": "Status and details of an async job." + }, + "JobResult": { + "properties": { + "response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Response", + "description": "AI response text (chat jobs)." + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session identifier (chat jobs)." + }, + "document_changes": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentChanges" + }, + { + "type": "null" + } + ], + "description": "Document modifications (chat jobs)." + }, + "usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/UsageInfo" + }, + { + "type": "null" + } + ], + "description": "Usage tracking data (chat jobs)." + }, + "attachment_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Attachment Id", + "description": "Processed attachment identifier (attachment processing jobs)." + } + }, + "additionalProperties": true, + "type": "object", + "title": "JobResult", + "description": "Job result data. Structure depends on job type." + }, + "LargeExportEmailRequest": { + "properties": { + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id" + }, + "format": { + "type": "string", + "title": "Format", + "default": "docx" + }, + "options": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Options" + }, + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filename" + }, + "source_filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Filename" + }, + "recipient_email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Recipient Email" + } + }, + "type": "object", + "title": "LargeExportEmailRequest", + "description": "Body for POST /v1/documents/export/email-request.\n\nUsed when a synchronous export exceeds the inline body cap. Send the same\npayload you would POST to /v1/documents/export (minus the html field \u2014\nthat comes from the session). The export is generated in the background\nand a secure download link is emailed within 24h." + }, + "LimitIncreaseRequest": { + "properties": { + "kind": { + "type": "string", + "enum": [ + "document_scale", + "session_scale", + "other" + ], + "title": "Kind", + "description": "What you hit: 'document_scale' = a single document crossed the per-document page limit (413 DOCUMENT_TOO_COMPLEX); 'session_scale' = the documents open in one chat crossed the per-chat limit (413 SESSION_TOO_FULL); 'other' = anything else (explain in note).", + "default": "document_scale" + }, + "attempted_pages": { + "anyOf": [ + { + "type": "integer", + "maximum": 10000000.0, + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Attempted Pages", + "description": "Approximate page count you were trying to work with (from the 413 message)." + }, + "attempted_sections": { + "anyOf": [ + { + "type": "integer", + "maximum": 10000000.0, + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Attempted Sections", + "description": "Exact section count from the 413 detail (section_count / document_section_count), if you have it." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "The file that hit the limit, if any." + }, + "note": { + "anyOf": [ + { + "type": "string", + "maxLength": 2000 + }, + { + "type": "null" + } + ], + "title": "Note", + "description": "Anything else that helps us size your limits (workload, cadence, deadline)." + } + }, + "type": "object", + "title": "LimitIncreaseRequest", + "description": "Body for request_limit_increase \u2014 the one-call limit-expansion request." + }, + "OpenDocumentsRequest": { + "properties": { + "document_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Document Ids", + "description": "Durable document ids (from GET /v1/documents) to open into the session." + } + }, + "type": "object", + "required": [ + "document_ids" + ], + "title": "OpenDocumentsRequest" + }, + "PendingChange": { + "properties": { + "change_id": { + "type": "string", + "title": "Change Id", + "description": "Unique identifier for this proposed change." + }, + "operation": { + "type": "string", + "title": "Operation", + "description": "Type of change: 'edit', 'create', or 'delete'." + }, + "chunk_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Chunk Id", + "description": "Target document section ID being modified or deleted." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Identifier of the document (tab) this change applies to \u2014 lets multi-document integrators attribute each section diff to the right document." + }, + "old_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Old Html", + "description": "Previous HTML content of the section (for updates)." + }, + "new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Html", + "description": "Proposed new HTML content (for updates and creates)." + }, + "ai_explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Explanation", + "description": "AI-generated explanation of why this change was proposed." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "change_id", + "operation" + ], + "title": "PendingChange", + "description": "A proposed document change awaiting user approval." + }, + "ProcessDocumentResponse": { + "properties": { + "html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Html" + }, + "session_id": { + "type": "string", + "title": "Session Id" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "chunks_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Chunks Count" + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id" + }, + "job_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Job Id" + }, + "status": { + "type": "string", + "title": "Status" + }, + "parse_mode": { + "type": "string", + "title": "Parse Mode" + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup" + }, + "warnings": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Warnings" + } + }, + "type": "object", + "required": [ + "session_id", + "filename", + "status", + "parse_mode" + ], + "title": "ProcessDocumentResponse" + }, + "ProcessUploadRequest": { + "properties": { + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Session ID to load this document/attachment into. Must match the chat/async session-id format \u2014 letters, digits, '_', '-', '.' only (no ':'); max 256 chars." + }, + "filename": { + "type": "string", + "maxLength": 255, + "title": "Filename", + "description": "Same filename passed to request_upload_url." + }, + "parse_mode": { + "type": "string", + "enum": [ + "document", + "attachment" + ], + "title": "Parse Mode", + "description": "'document' = parse and load as the active editable doc; 'attachment' = process asynchronously as AI-searchable reference (returns job_id to poll).", + "default": "document" + }, + "return_html": { + "type": "boolean", + "title": "Return Html", + "description": "When false (default), the response is compact \u2014 metadata only (chunks_count, version_id, page_setup), no document body \u2014 to keep your context small; the document is loaded into the session and you edit it via chat. Set true only if you actually need the parsed HTML returned inline.", + "default": false + } + }, + "type": "object", + "required": [ + "session_id", + "filename" + ], + "title": "ProcessUploadRequest" + }, + "ReEditChunkRequest": { + "properties": { + "user_current_html": { + "type": "string", + "title": "User Current Html" + }, + "ai_original_old_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Original Old Html" + }, + "ai_proposed_new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Proposed New Html" + }, + "ai_explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Explanation" + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier" + }, + "mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Mode", + "default": "redo" + } + }, + "type": "object", + "required": [ + "user_current_html" + ], + "title": "ReEditChunkRequest", + "description": "Re-apply the AI's intended change on the user's current section HTML, OR (mode='merge')\ncombine the user's version and the AI's version into one." + }, + "ReEditChunkResponse": { + "properties": { + "chunk_id": { + "type": "string", + "title": "Chunk Id" + }, + "new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Html" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + } + }, + "type": "object", + "required": [ + "chunk_id" + ], + "title": "ReEditChunkResponse" + }, + "RedeemRequest": { + "properties": { + "code": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "title": "Code", + "description": "The promo code (case-insensitive)." + } + }, + "type": "object", + "required": [ + "code" + ], + "title": "RedeemRequest", + "description": "Redeem a promo code and receive its credit grant." + }, + "RedeemResponse": { + "properties": { + "status": { + "type": "string", + "title": "Status", + "description": "Always 'redeemed' on success.", + "default": "redeemed" + }, + "promotion": { + "$ref": "#/components/schemas/RedemptionOut" + }, + "message": { + "type": "string", + "title": "Message" + } + }, + "type": "object", + "required": [ + "promotion", + "message" + ], + "title": "RedeemResponse", + "description": "Response body for POST /v1/promo/redeem on success." + }, + "RedemptionOut": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + } + }, + "type": "object", + "required": [ + "id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at" + ], + "title": "RedemptionOut", + "description": "Summary of a successful redemption, returned from POST /v1/promo/redeem." + }, + "RedoRequest": { + "properties": { + "turn_index": { + "type": "integer", + "minimum": 0.0, + "title": "Turn Index", + "description": "The turn the revert rewound to (rows at/after this were archived); redo un-archives exactly those." + }, + "redo_checkpoint_id": { + "type": "string", + "title": "Redo Checkpoint Id", + "description": "The identifier of the pre-revert state, returned by the revert response." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window issuing the request, used to coordinate concurrent reverts from multiple browser tabs." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "turn_index", + "redo_checkpoint_id" + ], + "title": "RedoRequest", + "description": "Undo a revert \u2014 restore the captured pre-revert state and the rolled-back\nconversation + document. Meant for use immediately after a revert (before sending a new message,\nwhich would diverge the timeline); the web app gates it to that window." + }, + "RevertRequest": { + "properties": { + "turn_index": { + "type": "integer", + "minimum": 0.0, + "title": "Turn Index", + "description": "Turn index of the user message to revert. The text of that message is returned in compose_text so the caller can re-edit and resend it. Every chat row at this turn or later is soft-archived (hidden from active reads but retained for audit)." + }, + "dry_run": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Dry Run", + "description": "When true, compute the per-document revert diff WITHOUT committing or archiving anything \u2014 used to render a preview before the user confirms.", + "default": false + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window issuing the revert, used to coordinate concurrent reverts from multiple browser tabs." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "turn_index" + ], + "title": "RevertRequest", + "description": "Rewind a chat session to the state immediately before a specific user message." + }, + "RevertResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier." + }, + "reverted_to_turn": { + "type": "integer", + "title": "Reverted To Turn", + "description": "Turn index that the active conversation now ends at (the AI reply preceding the reverted user message). -1 when the session is reset to its initial empty state (revert from the very first user message)." + }, + "compose_text": { + "type": "string", + "title": "Compose Text", + "description": "The text of the user message that was reverted. Clients should pre-fill the compose box with this so the user can edit and resend." + }, + "document_state": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentState" + }, + { + "type": "null" + } + ], + "description": "Restored document state. Null when the session is reset to empty." + }, + "editor_action": { + "type": "string", + "title": "Editor Action", + "description": "Client instruction: 'update' to load the restored document, or 'clear' to reset the editor to empty.", + "default": "update" + }, + "archived_turn_count": { + "type": "integer", + "title": "Archived Turn Count", + "description": "Number of chat rows soft-archived by this revert (turns hidden from the UI but retained for audit)." + }, + "revert_changes": { + "anyOf": [ + { + "additionalProperties": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Revert Changes", + "description": "Per-document (slug -> change-set) revert diff. Applying these merges the revert onto any concurrent live edits \u2014 a conflicting section is surfaced for you to resolve rather than replacing the whole document. On dry_run this is the preview; on a real revert it is what was committed." + }, + "dry_run": { + "type": "boolean", + "title": "Dry Run", + "description": "Echoes the request's dry_run: when true nothing was committed/archived \u2014 this is a preview only.", + "default": false + }, + "redo_checkpoint_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Redo Checkpoint Id", + "description": "The identifier of the pre-revert state. Revert is non-destructive \u2014 passing this back to POST /sessions/{id}/redo (with the same turn_index) restores it and un-archives the rolled-back turns, undoing the revert. Null on a dry-run or a first-message reset." + } + }, + "type": "object", + "required": [ + "session_id", + "reverted_to_turn", + "compose_text", + "archived_turn_count" + ], + "title": "RevertResponse", + "description": "Result of a session revert: restored document state and compose-box prefill." + }, + "SaveDocumentRequest": { + "properties": { + "html": { + "type": "string", + "title": "Html" + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup" + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id" + }, + "base_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Html" + }, + "touched_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Touched Chunk Ids" + }, + "deleted_part_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Deleted Part Chunk Ids" + } + }, + "type": "object", + "required": [ + "html" + ], + "title": "SaveDocumentRequest", + "description": "Body for the human-edit autosave." + }, + "SessionInfo": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Unique session identifier." + }, + "user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User who owns this session (null for organization sessions)." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the session was created." + }, + "last_activity": { + "type": "string", + "title": "Last Activity", + "description": "ISO 8601 timestamp of the most recent activity." + }, + "message_count": { + "type": "integer", + "title": "Message Count", + "description": "Total number of messages in this session." + }, + "preview": { + "type": "string", + "title": "Preview", + "description": "Preview of the first user message (up to 100 characters)." + } + }, + "type": "object", + "required": [ + "session_id", + "created_at", + "last_activity", + "message_count", + "preview" + ], + "title": "SessionInfo", + "description": "Summary of a document editing session." + }, + "SessionListResponse": { + "properties": { + "sessions": { + "items": { + "$ref": "#/components/schemas/SessionInfo" + }, + "type": "array", + "title": "Sessions", + "description": "Array of session summaries, ordered by most recent activity." + }, + "total": { + "type": "integer", + "title": "Total", + "description": "Total number of sessions returned." + } + }, + "type": "object", + "required": [ + "sessions", + "total" + ], + "title": "SessionListResponse", + "description": "List of document editing sessions." + }, + "UniversalChatRequest": { + "properties": { + "message": { + "type": "string", + "maxLength": 100000, + "title": "Message", + "description": "Your message to the AI assistant." + }, + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Unique session identifier. Reuse to continue a conversation." + }, + "document_html": { + "anyOf": [ + { + "type": "string", + "maxLength": 50000000 + }, + { + "type": "null" + } + ], + "title": "Document Html", + "description": "Current document HTML. OMIT this when the session already holds the document \u2014 the server persists it across turns, so you don't re-send it every turn; pass it only to load new content or replace the document wholesale (a verbatim load \u2014 the AI never re-types content passed here). Include data-chunk-id attributes on elements to enable targeted AI edits." + }, + "user_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User identifier. Automatically set from authentication \u2014 typically omit this." + }, + "image_attachments": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ImageAttachmentData" + }, + "type": "array", + "maxItems": 20 + }, + { + "type": "null" + } + ], + "title": "Image Attachments", + "description": "Inline images for vision-based analysis (base64-encoded)." + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier", + "description": "AI model to use: 'core' (default, fast), 'turbo' (fastest), 'pro' (advanced reasoning), 'max' (most capable)." + }, + "thinking_depth": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Thinking Depth", + "description": "Reasoning depth: 'fast', 'balanced' (default), or 'deep'. Controls how much analysis the AI performs." + }, + "approval_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Approval Mode", + "description": "Change review mode: 'approve_all' (default, auto-applies changes) or 'ask_every_time' (pauses for your review)." + }, + "response_mode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "full", + "compact" + ] + }, + { + "type": "null" + } + ], + "title": "Response Mode", + "description": "Response shape control. 'full' (default) returns the complete updated document HTML \u2014 required by web app editors and recommended for small documents (<20 pages). 'compact' (recommended for AI agents editing large documents) suppresses the full HTML and returns only per-section diffs (chunk_diffs) for changed sections, saving thousands of tokens per turn. To read sections in compact mode, just send a natural-language request like 'show me the force majeure section' \u2014 the AI returns the content in the chat reply text.", + "default": "full" + }, + "cursor_context": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Cursor Context", + "description": "Editor cursor context at message time, used to default the insert location for new images, diagrams, or sections when the user's prompt doesn't name a position. Shape: {chunk_id: str, pos_in_chunk?: int, text_before?: str, text_after?: str}. Omit when the user typed in chat without focusing the editor first." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Target a specific open document by id (ids come from /v1/sessions/{session_id}/documents). Omit to target the focused document (default \u2014 unchanged single-document behaviour). When set, that document becomes the focus for this turn." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window (set by the web app) so concurrent edits from two windows of the same chat are merged rather than overwritten. Agents/API may omit." + }, + "cross_session_memory": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory", + "description": "When true, the AI carries a private rolling memory of context across THIS account's chats (off by default). Per-request override of your saved Settings default." + }, + "cross_session_search": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Search", + "description": "When true, the AI may search your PAST chats and documents on demand to reuse prior work and open a found document (off by default). Per-request override of your saved Settings default; never affects the always-on search of the open document." + }, + "cross_session_memory_key": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory Key", + "description": "Optional per-request id of YOUR end-customer so the cross-session memory note is kept in a separate file per end-customer. Omit for one account-level note (the default / B2C)." + }, + "cross_session_scope": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 2000 + }, + { + "type": "null" + } + ], + "title": "Cross Session Scope", + "description": "Optional list of session ids to restrict cross-session SEARCH to (e.g. one end-customer's sessions). Omit to search all of this account's sessions. Only narrows within the API-key owner; ignored unless cross_session_search is on." + }, + "touched_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Touched Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) the user actually edited since the last sync, sent alongside document_html. When present, a chunk NOT in this list whose text is unchanged keeps its stored formatting byte-for-byte even if the client serialized it differently (protects styling from editor round-trip loss). Chunks in the list \u2014 and any chunk whose text changed \u2014 always take the submitted content. Omit for the default behavior (any differing chunk is treated as an edit)." + }, + "deleted_part_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Deleted Part Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) of OUT-OF-FLOW part sections (page headers/footers, footnote/endnote bodies, comments) to delete explicitly, sent alongside document_html. Out-of-flow parts absent from document_html are always KEPT \u2014 an editor view not containing them is their normal state, never a deletion \u2014 so removing one requires naming its id here. Ids that are not stored out-of-flow parts are ignored (in-flow content keeps the default behavior). Omit when deleting nothing." + } + }, + "type": "object", + "required": [ + "message", + "session_id" + ], + "title": "UniversalChatRequest", + "description": "Send a message to the AI assistant with optional document context.", + "examples": [ + { + "approval_mode": "approve_all", + "document_html": "

Section 3

Terms and conditions...

", + "message": "Add a confidentiality clause to section 3", + "model_tier": "core", + "session_id": "session_abc123" + } + ] + }, + "UpdateDocumentRequest": { + "properties": { + "title": { + "anyOf": [ + { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + { + "type": "null" + } + ], + "title": "Title", + "description": "New document title." + }, + "parts": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Parts", + "description": "Document-parts patch \u2014 the document's OUT-OF-FLOW content, keyed by any of: sections, headers, footers, footnotes, endnotes, comments. Each key you send REPLACES that whole part family; a null value clears it; keys you omit are untouched. Every HTML fragment is sanitized server-side. Headers/footers are keyed by section index with default/first/even variants; dynamic page fields use cached text." + }, + "base_parts": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Base Parts", + "description": "Optional concurrency guard: the parts subtree your edit was based on (as last read). If another writer changed one of the same part families since, your write still lands (newest-write-wins) and the overwritten families are reported in `parts_conflicts`." + } + }, + "type": "object", + "title": "UpdateDocumentRequest", + "description": "PATCH /v1/documents/{id} body \u2014 rename a document and/or update its out-of-flow\nparts. Both fields optional; at least one must be present." + }, + "UpdateProfileRequest": { + "properties": { + "display_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Display Name", + "description": "New display name." + }, + "timezone": { + "anyOf": [ + { + "type": "string", + "maxLength": 100 + }, + { + "type": "null" + } + ], + "title": "Timezone", + "description": "IANA timezone (e.g., 'America/New_York')." + }, + "language": { + "anyOf": [ + { + "type": "string", + "maxLength": 10 + }, + { + "type": "null" + } + ], + "title": "Language", + "description": "Preferred language code (e.g., 'en', 'es')." + }, + "preferences": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Preferences", + "description": "User preferences to update (e.g., {approval_mode: 'ask_every_time', model_tier: 'pro'})." + } + }, + "type": "object", + "title": "UpdateProfileRequest", + "description": "Update user profile fields." + }, + "UploadImageBase64Request": { + "properties": { + "image_base64": { + "type": "string", + "title": "Image Base64", + "description": "The image bytes, base64-encoded (raw base64 or a data: URL)." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Optional original filename (used only to infer content type)." + }, + "content_type": { + "anyOf": [ + { + "type": "string", + "maxLength": 100 + }, + { + "type": "null" + } + ], + "title": "Content Type", + "description": "Optional MIME type, e.g. image/png. Defaults to image/png." + } + }, + "type": "object", + "required": [ + "image_base64" + ], + "title": "UploadImageBase64Request" + }, + "UploadUrlRequest": { + "properties": { + "filename": { + "type": "string", + "maxLength": 255, + "title": "Filename", + "description": "Original filename including extension (e.g. 'contract.docx'). Used downstream for file-type detection." + }, + "content_type": { + "type": "string", + "maxLength": 200, + "title": "Content Type", + "description": "MIME type the agent will PUT with (e.g. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' for .docx)." + }, + "size_bytes": { + "type": "integer", + "maximum": 104857600.0, + "exclusiveMinimum": 0.0, + "title": "Size Bytes", + "description": "File size in bytes. Must be > 0 and <= 104857600 (100 MB)." + }, + "purpose": { + "type": "string", + "enum": [ + "document", + "attachment", + "export-html" + ], + "title": "Purpose", + "description": "What this file will be used for. 'document' = the active editable doc; 'attachment' = read-only AI-searchable reference; 'export-html' = HTML payload destined for /v1/documents/export (large-export upload flow, for documents above ~25 MB).", + "default": "document" + } + }, + "type": "object", + "required": [ + "filename", + "content_type", + "size_bytes" + ], + "title": "UploadUrlRequest" + }, + "UploadUrlResponse": { + "properties": { + "upload_id": { + "type": "string", + "title": "Upload Id" + }, + "upload_url": { + "type": "string", + "title": "Upload Url" + }, + "expires_at": { + "type": "string", + "title": "Expires At" + }, + "expires_in_seconds": { + "type": "integer", + "title": "Expires In Seconds" + }, + "max_size_bytes": { + "type": "integer", + "title": "Max Size Bytes" + }, + "curl_example": { + "type": "string", + "title": "Curl Example" + } + }, + "type": "object", + "required": [ + "upload_id", + "upload_url", + "expires_at", + "expires_in_seconds", + "max_size_bytes", + "curl_example" + ], + "title": "UploadUrlResponse" + }, + "UsageInfo": { + "properties": { + "monthly_used": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Used", + "description": "Total operations used this billing cycle." + }, + "monthly_limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Limit", + "description": "Maximum operations allowed per cycle. -1 means unlimited." + }, + "monthly_remaining": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Remaining", + "description": "Operations remaining this cycle. -1 means unlimited." + }, + "was_billable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Was Billable", + "description": "Whether this request counted as a billable operation." + }, + "ops_charged": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Ops Charged", + "description": "How many operations this request billed. A large multi-section edit can bill more than one (one per 25 sections edited), so monthly_used can increase by more than 1 between responses." + }, + "quota_exhausted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Quota Exhausted", + "description": "True when you have reached your plan's operation limit with no remaining balance \u2014 the current request still completes, but further billable requests pause until you upgrade or your billing cycle resets." + }, + "subscription_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Subscription Tier", + "description": "Current subscription tier: 'free', 'plus', 'pro', or 'enterprise'." + }, + "bucket_used": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bucket Used", + "description": "Which bucket this operation drew from: 'tier' or 'promo'. Null for non-billable ops." + }, + "redemption_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Redemption Id", + "description": "Redemption grant this op drew from, when bucket_used is 'promo'." + }, + "promotions": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ActivePromotionInfo" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Promotions", + "description": "User's currently-active promotion grants, ordered by oldest-expiring first." + } + }, + "additionalProperties": true, + "type": "object", + "title": "UsageInfo", + "description": "Operation usage data for the current billing cycle." + }, + "UsageStatsResponse": { + "properties": { + "user_id": { + "type": "string", + "title": "User Id", + "description": "User identifier." + }, + "subscription_tier": { + "type": "string", + "title": "Subscription Tier", + "description": "Current plan: 'free', 'plus', 'pro', or 'enterprise'." + }, + "monthly_limit": { + "type": "integer", + "title": "Monthly Limit", + "description": "Maximum operations allowed per month. -1 means unlimited." + }, + "monthly_used": { + "type": "integer", + "title": "Monthly Used", + "description": "Operations used this billing cycle." + }, + "monthly_remaining": { + "type": "integer", + "title": "Monthly Remaining", + "description": "Operations remaining this cycle." + }, + "monthly_reset_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Monthly Reset At", + "description": "ISO 8601 timestamp when the monthly counter resets." + }, + "total_sessions": { + "type": "integer", + "title": "Total Sessions", + "description": "Total number of chat sessions created." + }, + "total_documents": { + "type": "integer", + "title": "Total Documents", + "description": "Total number of documents processed." + }, + "total_operations": { + "type": "integer", + "title": "Total Operations", + "description": "Lifetime total operations across all billing cycles." + }, + "current_month_stats": { + "additionalProperties": true, + "type": "object", + "title": "Current Month Stats", + "description": "Breakdown of this month's operations by type, including counts, tokens used, and success rates." + } + }, + "type": "object", + "required": [ + "user_id", + "subscription_tier", + "monthly_limit", + "monthly_used", + "monthly_remaining", + "total_sessions", + "total_documents", + "total_operations", + "current_month_stats" + ], + "title": "UsageStatsResponse", + "description": "Detailed usage statistics for the current billing cycle." + }, + "UserProfileResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Unique user identifier (UUID)." + }, + "email": { + "type": "string", + "title": "Email", + "description": "User's email address." + }, + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Display Name", + "description": "User's display name." + }, + "photo_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Photo Url", + "description": "URL to user's profile photo." + }, + "email_verified": { + "type": "boolean", + "title": "Email Verified", + "description": "Whether the user's email has been verified." + }, + "auth_provider": { + "type": "string", + "title": "Auth Provider", + "description": "Authentication method: 'email' or 'google'." + }, + "subscription_tier": { + "type": "string", + "title": "Subscription Tier", + "description": "Current plan: 'free', 'plus', 'pro', or 'enterprise'." + }, + "subscription_status": { + "type": "string", + "title": "Subscription Status", + "description": "Subscription status: 'active', 'canceled', or 'past_due'." + }, + "monthly_operation_limit": { + "type": "integer", + "title": "Monthly Operation Limit", + "description": "Maximum operations allowed per month. -1 means unlimited." + }, + "monthly_operations_used": { + "type": "integer", + "title": "Monthly Operations Used", + "description": "Operations used this billing cycle." + }, + "monthly_remaining": { + "type": "integer", + "title": "Monthly Remaining", + "description": "Operations remaining this cycle." + }, + "preferences": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Preferences", + "description": "User preferences (e.g., approval_mode, model_tier)." + }, + "created_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At", + "description": "ISO 8601 timestamp when the account was created." + }, + "last_login_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Login At", + "description": "ISO 8601 timestamp of the last login." + } + }, + "type": "object", + "required": [ + "id", + "email", + "email_verified", + "auth_provider", + "subscription_tier", + "subscription_status", + "monthly_operation_limit", + "monthly_operations_used", + "monthly_remaining" + ], + "title": "UserProfileResponse", + "description": "User profile with subscription and usage information." + }, + "UserPromotionsResponse": { + "properties": { + "active": { + "items": { + "$ref": "#/components/schemas/ActivePromotionOut" + }, + "type": "array", + "title": "Active" + }, + "history": { + "items": { + "$ref": "#/components/schemas/HistoricalPromotionOut" + }, + "type": "array", + "title": "History" + } + }, + "type": "object", + "required": [ + "active", + "history" + ], + "title": "UserPromotionsResponse", + "description": "Listing of the authenticated user's promotion grants, split by active vs. history." + }, + "UserSessionResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Unique session identifier." + }, + "last_message_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Message At", + "description": "ISO 8601 timestamp of the most recent message." + }, + "message_count": { + "type": "integer", + "title": "Message Count", + "description": "Total number of messages in this session." + } + }, + "type": "object", + "required": [ + "session_id", + "message_count" + ], + "title": "UserSessionResponse", + "description": "Summary of a user's chat session." + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + }, + "securitySchemes": { + "HTTPBearer": { + "type": "http", + "scheme": "bearer" + } + } + } +} \ No newline at end of file diff --git a/extensions/doctask2-oviya-senthilkumar/specs/superdocs-new.json b/extensions/doctask2-oviya-senthilkumar/specs/superdocs-new.json new file mode 100644 index 00000000..e0da9235 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/specs/superdocs-new.json @@ -0,0 +1,8505 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Universal Document AI API", + "description": "AI-powered document editing with multi-tenant organization support", + "version": "2.0.0" + }, + "paths": { + "/v1/chat": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Edit, draft, or restructure a document using natural language. Preserves tables, styling, and formatting.", + "description": "Synchronous AI chat that can rewrite specific paragraphs, add or remove table rows, restructure sections, generate new content from templates, or transform an entire document. Pass document_html only to load or replace the document; once a session holds a document the server persists it across turns, so omit it on follow-up turns. To replace the whole document with EXACT content you already hold, pass that content in document_html (a verbatim load \u2014 the AI never re-types it) or upload it via upload_document_base64; describing the content only in `message` makes the AI author it, which can drift from your text. Returns AI response text plus structural document changes (HTML edits, additions, deletions) with chunk IDs. One billable operation per document-modifying turn; very large multi-section edits bill one operation per 25 sections changed. For long-running edits or human-in-the-loop approval, use chat_async. Optional: model_tier (core/turbo/pro/max), thinking_depth (fast/balanced/deep), image_attachments for multimodal vision. RECOMMENDED for AI agents working with large documents (>20 pages): set response_mode='compact' to skip the full HTML in the response (the AI returns only per-section diffs in chunk_diffs) and save thousands of tokens per turn. To read sections in compact mode, just send a natural-language request like 'show me the force majeure section' \u2014 the AI returns the content in the reply text. Always use natural language to describe what you want; the AI handles all internal section lookups.", + "operationId": "chat", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UniversalChatRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "List your active document editing sessions to resume or audit prior work.", + "description": "Returns sessions sorted by most recent activity, with message counts and last-updated timestamps. Each session represents one document with full edit history and AI conversation persisted server-side. Use to find a previous editing context to resume (then call get_session_history) or to audit your workspace.", + "operationId": "list_sessions", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter by user ID (deprecated - auth determines scope)", + "title": "User Id" + }, + "description": "Filter by user ID (deprecated - auth determines scope)" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of sessions to return", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of sessions to return" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents": { + "get": { + "tags": [ + "v1", + "mcp" + ], + "summary": "List your saved documents (the Files view).", + "description": "List the authenticated user/org's saved documents, most-recently-updated first\n(metadata only \u2014 no document content). Each carries a session_count (\"N chats\"). Documents\nbecome durable + reusable automatically as you create or edit them; on the FIRST call we also\none-time import the documents of pre-Files-view chats so prior work appears here too.", + "operationId": "list_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1, + "description": "Maximum number of documents to return.", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of documents to return." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Offset for pagination.", + "default": 0, + "title": "Offset" + }, + "description": "Offset for pagination." + }, + { + "name": "include_preview", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include a small preview_html (first chunks) per document for thumbnails. The web Files view sets this; agents leave it off to stay token-light.", + "default": false, + "title": "Include Preview" + }, + "description": "Include a small preview_html (first chunks) per document for thumbnails. The web Files view sets this; agents leave it off to stay token-light." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/cross-session-memory": { + "delete": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Clear your cross-session memory note.", + "description": "Delete the caller's cross-session memory note. Owner-scoped: a caller can only clear its OWN\nnote. With no key this clears the account-level note; with a memory_key it clears that one\nend-customer's note. Idempotent (removed=0 if it didn't exist).", + "operationId": "clear_cross_session_memory", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "memory_key", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Clear ONE end-customer's stored memory (the cross_session_memory_key you send on chat). Omit to clear the account-level note (the B2C default).", + "title": "Memory Key" + }, + "description": "Clear ONE end-customer's stored memory (the cross_session_memory_key you send on chat). Omit to clear the account-level note (the B2C default)." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/{document_id}": { + "get": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Get a saved document's detail, structure outline, and the chats that used it.", + "description": "One saved document's metadata plus a STRUCTURE outline and the chat sessions that have it\nopen (prior-chats-per-file). Owner-scoped \u2014 404 if the document isn't yours.\n\n`structure` (always included, token-light, NON-BILLABLE) is the cheap verify step after\nany edit: `headings` (level + text + position), `section_count` (heading-anchored), `block_count`\n(editable chunks), `media` (image/diagram counts). It answers \"did my edit land?\" for free \u2014\nnever export a whole document just to check its structure. Pass include_html=true only when\nyou need the body.", + "operationId": "get_document_detail", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, also returns the document's full reassembled HTML body (`html`), plus its `page_setup` and `version_id`. Default false returns metadata + structure + prior-chats only, keeping the response token-light. This is the by-id way to read a saved document's body without opening it into a session.", + "default": false, + "title": "Include Html" + }, + "description": "When true, also returns the document's full reassembled HTML body (`html`), plus its `page_setup` and `version_id`. Default false returns metadata + structure + prior-chats only, keeping the response token-light. This is the by-id way to read a saved document's body without opening it into a session." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "patch": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Rename a saved document and/or update its out-of-flow parts (headers, footers, footnotes, endnotes, comments, sections).", + "description": "Rename one of your saved documents and/or update its out-of-flow parts. Parts are\nthe document's out-of-flow content \u2014 headers/footers, footnote and endnote bodies,\ncomments, and per-section page geometry. Every fragment is sanitized on write, writes\nare versioned and safe under concurrent editors, and parts revert with the document.\nA title-only call behaves exactly as the original rename (response `status:\"renamed\"`).\nPart content lives in the document body and is edited exactly like any other document\ncontent (via chat or the document write endpoints); a legacy `parts` payload returns\n400 `parts_moved_to_chunks` pointing you there.", + "operationId": "rename_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDocumentRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Delete (archive) a saved document.", + "description": "Soft-archive a saved document \u2014 it leaves the Files view but is recoverable server-side.\nPresence-aware: if the document is currently open in ANOTHER session and `force` is not set,\nthe request FAILS honestly with HTTP 409 (`code:\"document_in_use\"` + `open_in_sessions:N` +\n`suggested_action`) so callers can confirm \"open in N other session(s) \u2014 delete anyway?\" and\nre-call with `force=true`. On `force=true` it archives, unlinks every session, and notifies the\nother sessions (which then prompt the user to keep editing or honor the deletion).\n\nA no-op never wears a success status: if nothing was archived you get the 409 above, so a\n2xx always means the archive actually happened.", + "operationId": "archive_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "force", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Confirm deletion of a doc that is open in another session. Without it, deleting an in-use doc fails with HTTP 409 (code document_in_use, open_in_sessions:N) instead of tombstoning \u2014 nothing is archived until you re-call with force=true.", + "default": false, + "title": "Force" + }, + "description": "Confirm deletion of a doc that is open in another session. Without it, deleting an in-use doc fails with HTTP 409 (code document_in_use, open_in_sessions:N) instead of tombstoning \u2014 nothing is archived until you re-call with force=true." + }, + { + "name": "from_session", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The caller's own session id (so it is excluded from the in-use check). Omit for a pure Files-view delete.", + "title": "From Session" + }, + "description": "The caller's own session id (so it is excluded from the in-use check). Omit for a pure Files-view delete." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/{document_id}/unarchive": { + "post": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Restore (un-archive) a previously archived document by its id.", + "description": "Restore (un-archive) a saved document by its id \u2014 the mirror of archive_document. The document\nre-enters your Files view and can be opened/edited again (open it into a chat with open_documents).\n`document_id` is the same id used by list_documents / archive_document. Idempotent: restoring an\nalready-active (or unknown) document is a no-op. Non-billable.", + "operationId": "unarchive_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/open": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Open saved documents into a chat session (shared, never copied).", + "description": "Load one or more SAVED documents into a session as editable tabs. The SAME durable\ndocument is attached (never copied), so edits flow back to the one shared row with\ncross-session soft-collaboration. The first listed document is focused. Returns the\nrefreshed document roster so the client can render tabs immediately.", + "operationId": "open_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenDocumentsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/init": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Start a new chat session and open documents into it in one call.", + "description": "Create a session AND open N saved documents into it in ONE call (the documents are SHARED,\nnever copied \u2014 the same cross-session behavior as /documents/open). The web UI passes its own\nsession_id so its session model stays consistent; an MCP/API integrator can omit it and the\nserver mints one \u2014 the one-call way to start a session with documents already open. With no\ndocument_ids it just returns a fresh, empty session. The open semantics (first document focused,\ndurable binding, returned roster) are identical to /documents/open.", + "operationId": "init_session", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InitSessionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/save": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Persist a user-edited document (non-AI autosave).", + "description": "Persist a HUMAN-edited document \u2014 the editor's debounced autosave + save-on-blur \u2014 WITHOUT\nan AI turn. Re-indexes the html into the target document (preserving its durable identity so it\nUPDATES the same Files entry), then saves it so pure typing is preserved AND other sessions with\nthe same document open stay in sync. The AI-edit flow is unaffected (autosave saves first; a\nlater AI result is merged with your saved edits). Non-billable, REST-only (a UI affordance, not an MCP tool).", + "operationId": "save_human_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SaveDocumentRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/unarchive": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Restore a document another session archived, keeping your current edits (web app).", + "description": "Restore (un-archive) a document that was archived, re-linking it to this session and bringing\nits content back. `document_id` is the session-local id (same as /save); the durable id is\nrecovered from the cached document. Idempotent: restoring an already-active (or never-archived)\ndocument is a no-op. Non-billable.\n\nTwo shapes: WITHOUT a body (e.g. an AI agent restoring a document by id) the archived content is\nrestored as-is; WITH a body carrying the current editor HTML (the web app's \"keep editing\n(restores it)\" choice after another session deleted the document) the document is restored AND the\nsupplied edits are saved on top, so it converges for everyone.", + "operationId": "restore_document_keep_editing", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SaveDocumentRequest" + }, + { + "type": "null" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/blank": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Open a new blank document as a tab in the session.", + "description": "Open a fresh BLANK document as a new focused tab in the session (the tab-strip \"+\").\nNon-billable \u2014 no AI, no upload pipeline; it is saved on first edit.\nREST-only (a manual UI affordance, not an MCP tool \u2014 agents create documents through chat).", + "operationId": "new_blank_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "List the documents open in a multi-document session.", + "description": "Returns the editable documents open in this session (focused first), each with its id, chunk\ncount, and focused flag \u2014 so a client can render document tabs. The response is token-light by\ndefault; pass include_html=true to also get each document's reassembled HTML.\n\nID MAPPING: `document_id` is the SESSION-LOCAL slot id (\"doc_primary\", \"doc_ab12\u2026\");\n`durable_document_id` is the PERMANENT documents.id UUID \u2014 the SAME id `list_documents` (Files)\nshows and `get_document_detail` / `rename_document` / `archive_document` / `open_documents`\ntake (null until the document's first save). focus_session_document, close_session_document,\nand chat's `document_id` accept EITHER form, so you can drive a session entirely with durable\nids.", + "operationId": "list_session_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "report_changed", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Resume awareness: when true, each doc carries `changed_since_seen` = its committed version is newer than the version THIS session last saw (changed by another session while away). The web UI sets this ONLY on resume / a chat switch (never on the ~2s poll) so it can show a one-time 'changed since you were last here' notice \u2014 ongoing changes are surfaced through the normal change feed. Computed before the roster updates the version this session has seen.", + "default": false, + "title": "Report Changed" + }, + "description": "Resume awareness: when true, each doc carries `changed_since_seen` = its committed version is newer than the version THIS session last saw (changed by another session while away). The web UI sets this ONLY on resume / a chat switch (never on the ~2s poll) so it can show a one-time 'changed since you were last here' notice \u2014 ongoing changes are surfaced through the normal change feed. Computed before the roster updates the version this session has seen." + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/doc-events": { + "get": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Session Document Events", + "description": "Poll for cross-session document updates: returns `document_updated` events for documents\nTHIS session holds that OTHER sessions committed since `after_id`. The client polls this\n(~every 1-2s while a doc is open) and re-fetches the changed document's content so all open\nsessions converge without a refresh. Excludes this session's own writes by default (the web UI\nalready has its own changes); a REST/MCP integrator can pass `include_own=true` to receive its\nown events too. REST-only \u2014 a lightweight poll, deliberately NOT an MCP tool and NOT a held\nSSE connection (keeps the per-instance connection budget free).", + "operationId": "session_document_events_v1_sessions__session_id__doc_events_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "after_id", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Return events with id greater than this (the poll cursor).", + "default": 0, + "title": "After Id" + }, + "description": "Return events with id greater than this (the poll cursor)." + }, + { + "name": "include_own", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Integrator opt-out: when true, ALSO return this session's own document_updated events (default false excludes them \u2014 the web UI never needs its own writes echoed). A REST/MCP integration that writes on one connection and polls on another, or wants a complete change feed for the docs it holds, sets this true.", + "default": false, + "title": "Include Own" + }, + "description": "Integrator opt-out: when true, ALSO return this session's own document_updated events (default false excludes them \u2014 the web UI never needs its own writes echoed). A REST/MCP integration that writes on one connection and polls on another, or wants a complete change feed for the docs it holds, sets this true." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/focus": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Switch which document is focused in a multi-document session.", + "description": "Make document_id the focused document (the editor's active document and the default edit\ntarget). Accepts either the session slot id OR the durable documents.id UUID (the id shown by\nlist_documents/Files). Persists the outgoing focused document into the session's\ndocument map and returns the now-focused document's HTML.", + "operationId": "focus_session_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Close (remove) a document from a multi-document session.", + "description": "Remove document_id from the session's open documents (the tab close button). If it's the\nfocused document, focus another open document \u2014 `next_focus` if it's still open (so the client\ncan pick the adjacent tab), else the next remaining one; closing the last document leaves the\nsession empty. Returns the updated document roster + the now-focused document's HTML.\n\nThe close takes effect immediately and is persisted, so a later reconnect or session restore\nwon't bring the closed document back. Persistence is best-effort \u2014 a failure is logged, not\nfatal (the close still holds for the active session).", + "operationId": "close_session_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "next_focus", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Next Focus" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/chunks/{chunk_id}/re-edit": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Re-apply the AI's intended edit on top of the user's current version of one section.", + "description": "Resolve a concurrent-edit conflict on ONE section. When a user edited a section while the AI\nwas also changing it, this re-applies the AI's intended change on top of the user's current text\n(mode=\"redo\"), or blends the user's and AI's versions into one (mode=\"merge\"). Returns only the\nrewritten section HTML. Performs one AI edit and counts as one billable operation.", + "operationId": "re_edit_chunk_on_user_version", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "chunk_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Chunk Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReEditChunkRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReEditChunkResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/history": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Restore the full conversation and document state for a previous session.", + "description": "Returns complete message history (user and AI), final document HTML with chunk IDs preserved, attachment list, and editor actions (font, color, alignment changes). Use to continue editing a document you started in a prior session. The AI rehydrates with full context of all prior decisions, chunk IDs, and attachments. Pass include_document_html=false to skip the full document body when you only need the conversation.", + "operationId": "get_session_history", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "focus_document_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Durable id of the document to focus on restore (e.g. the file the user clicked in the Files view). When set, that document is shown in the editor and marked focused in the roster; omit to use the session's own last-focused document.", + "title": "Focus Document Id" + }, + "description": "Durable id of the document to focus on restore (e.g. the file the user clicked in the Files view). When set, that document is shown in the editor and marked focused in the roster; omit to use the session's own last-focused document." + }, + { + "name": "include_document_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When false, omit the restored document's full HTML body (document_state.html_content) to keep your context small \u2014 agents that just need the conversation + metadata should set false. Default true (the web app needs the body to re-render the editor).", + "default": true, + "title": "Include Document Html" + }, + "description": "When false, omit the restored document's full HTML body (document_state.html_content) to keep your context small \u2014 agents that just need the conversation + metadata should set false. Default true (the web app needs the body to re-render the editor)." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnhancedChatHistoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/revert": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Rewind a chat session to before a specific user message \u2014 restores both the document and the conversation in one call.", + "description": "Rewinds a chat session to the state immediately before a specific user message. The document, conversation history, and supporting context all snap back to that point. The text of the reverted message is returned (compose_text) so the caller can edit and resend it. Messages from the reverted turn forward are soft-archived: hidden from active reads but retained for audit. The original conversation is preserved server-side; the restored conversation becomes the active timeline. Rejects with 409 if the session has a chat job in progress or awaiting approval \u2014 wait for the job to settle before reverting. Returns 422 if the message predates the revertability feature.", + "operationId": "revert_session_to_message", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/redo": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Undo a revert: restore the pre-revert state and the rolled-back turns.", + "description": "Revert is non-destructive: this restores the session FORWARD to the pre-revert state\n(returned by the revert as redo_checkpoint_id), restores the document to that state while still\nmerging any concurrent edits from other sessions (keeping both where they conflict), and\nun-archives exactly the chat turns the revert hid. Intended for use immediately after a revert;\nonce a new message is sent the timeline diverges and the web app stops offering it.", + "operationId": "redo_revert", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedoRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/upload": { + "post": { + "tags": [ + "v1", + "attachments" + ], + "summary": "Upload Attachment", + "description": "Upload a document attachment to a session for AI reference.\n\nThe file is processed asynchronously \u2014 text is extracted, converted, and indexed\nso the AI can search and reference it during chat. Use GET /v1/attachments/status/{session_id}\nto check processing progress.\n\nSupported file types: .pdf, .docx, .txt, .rtf, .md, .html, .htm (max 50 MB).\nReturns a job_id for tracking the processing status.", + "operationId": "upload_attachment", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_attachment" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/{attachment_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Remove an attachment from a session or cancel its in-progress processing.", + "description": "Removes the attachment from the session; the AI will no longer reference it in subsequent chat turns. If processing is still in progress, also cancels the underlying job. Use to free up context, remove sensitive files mid-session, or replace a stale reference document. The attachment_id can be either the final attachment ID or the job ID (during processing).", + "operationId": "delete_attachment", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "attachment_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Attachment Id" + } + }, + { + "name": "session_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/status/{session_id}": { + "get": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Check processing status of all attachments in a session.", + "description": "Returns each attachment's processing state (pending/processing/completed/failed) plus extracted text length and chunk count once ready. Poll this after upload_attachment_base64 to know when an attachment becomes queryable by the AI. Surfaces processing errors with actionable messages.", + "operationId": "get_attachment_status", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/upload": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Upload Document To Editor", + "description": "Upload a document file and load it into the editor.\n\nConverts the file to HTML with formatting preserved (tables, colors, images).\nImages are extracted to cloud storage and referenced by URL.\nReturns the HTML for the editor to display.\n\nAI indexing happens automatically on the first chat message.\nFor API clients who want immediate indexing, pass ?index=true.", + "operationId": "upload_document_to_editor", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "index", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Set to 'true' for immediate AI indexing (API clients). Default: conversion only.", + "title": "Index" + }, + "description": "Set to 'true' for immediate AI indexing (API clients). Default: conversion only." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_document_to_editor" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/images/upload": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Upload an image and get back a stable URL the editor can drop into .", + "description": "Upload a single inline image and return a stable URL you can reference\nin the document via .\n\nAccepts PNG/JPEG/WebP/GIF/SVG, up to 10 MB per upload. The returned URL is\npublic-read with an unguessable path. Useful for saving a drawing or\nscreenshot so a document can embed it.", + "operationId": "upload_inline_image", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_inline_image" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/images/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Upload a base64-encoded image and get back a stable URL to embed in a document via .", + "description": "Upload an image (base64-encoded) and get a stable public URL to reference\nin a document via .\n\nAccepts PNG/JPEG/WebP/GIF/SVG, up to 10 MB decoded. Send raw base64 or a\ndata: URL in image_base64. This is the agent-friendly counterpart to the\nbrowser multipart upload \u2014 use it to save a generated or fetched image so a\ndocument can embed it.", + "operationId": "upload_image_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadImageBase64Request" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/limits/increase-request": { + "post": { + "tags": [ + "v1", + "mcp", + "account" + ], + "summary": "Request higher document/chat scale limits in one call \u2014 no email needed.", + "description": "Request a higher scale limit for your account in ONE call.\n\nWHEN TO USE: after any 413 with error_code DOCUMENT_TOO_COMPLEX (a single\ndocument crossed the standard per-document page limit) or SESSION_TOO_FULL\n(the documents open in one chat crossed the standard per-chat limit). These\nare stability limits, not hard caps: the platform supports larger\ndeployments, and limits are raised per account on request.\n\nWHAT HAPPENS: the SuperDocs team is notified immediately with your account\nidentity and the numbers you send; limits are typically expanded within a\nday and you are contacted at your account email. No email writing needed \u2014\nthough hello@superdocs.app also works if you prefer.\n\nALTERNATIVES while you wait: split the file into smaller documents, upload\nit as an attachment (reference/search, not editing), start another session\nfor additional documents (init_session), or close documents you no longer\nneed (close_session_document).", + "operationId": "request_limit_increase", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LimitIncreaseRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/export": { + "post": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Export the current document as a styled .docx (default), .pdf, .html, .md, or .txt file with full fidelity.", + "description": "Round-trips the document through the original docx renderer, preserving tables, borders, shading, alternating row colors, headers, footers, fonts, inline styling, and embedded images. Two modes: pass html directly to export ad-hoc content, OR pass session_id to export the session's current state. Format options: 'docx' (default, native Open XML, best for programmatic processing or mail merge), 'pdf', 'html', 'markdown', or 'txt' ('doc' is a legacy Word-compatible HTML alias). Fidelity is the differentiator vs naive HTML to docx converters.", + "operationId": "export_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExportRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/export/email-request": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Request Large Export Email", + "description": "Enqueue a large-export job \u2014 runs in the background and emails a\nsecure, time-limited download link when the export finishes. 24h SLA\npromised in the email.", + "operationId": "request_large_export_email", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LargeExportEmailRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "documents" + ], + "summary": "Upload .docx/PDF/HTML/MD/RTF as the active editable document with chunk-ID structural editing.", + "description": "Parses the file into structured HTML where every paragraph, heading, table, row, and cell has a unique chunk ID, enabling the AI to make targeted structural edits via chat (\"remove row 3 of the pricing table\" works). Tables, borders, shading, alternating row colors, fonts, and inline styling are preserved on edit and export. Also works for AI-generated content: if you've drafted an outline or partial document in your context, upload it here as the working doc, then use chat to fill in the rest. This is also the reliable way to REPLACE a session's document with exact content you already hold \u2014 the upload is a verbatim load, so it can never come back paraphrased or placeholder-shaped the way asking chat to re-type it can. When you pass a session_id the response is compact by default (metadata only \u2014 pass return_html=true for the full parsed HTML); the document is loaded into the session and you edit it via chat. For files >100KB, prefer request_upload_url (pre-signed URL flow) to avoid token bloat from base64 through the agent context. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm. Max 50 MB validated; note the hosted transport layer caps any REQUEST BODY at ~32 MB and rejects larger ones at the gateway with an HTML (not JSON) error \u2014 base64 inflates content ~33%, so inline uploads are reliable up to ~20 MB of file bytes; beyond that use request_upload_url (pre-signed flow, up to 100 MB).", + "operationId": "upload_document_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Upload a reference file (PDF/DOCX/image) for the AI to query while editing.", + "description": "Files are processed asynchronously and become AI-searchable once ready. The AI can then reference the attachment's content during chat (e.g., \"rewrite section 3 to match the style guide PDF I attached\"). Images are queryable via multimodal vision. Poll get_attachment_status to know when ready. Distinct from upload_document_base64: attachments are read-only context for the AI to reference, not the editable working document. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm. Max 50 MB validated; note the hosted transport layer caps any REQUEST BODY at ~32 MB and rejects larger ones at the gateway with an HTML (not JSON) error \u2014 base64 inflates content ~33%, so inline uploads are reliable up to ~20 MB of file bytes; beyond that use request_upload_url (pre-signed flow, up to 100 MB). Requires session_id.", + "operationId": "upload_attachment_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "Save a document template (NDA, contract, SOP, letterhead) for reuse across sessions.", + "description": "Templates persist across sessions and can be referenced by the AI when drafting new documents (e.g., \"draft an NDA using my standard template\"). Stored at user or organization scope. Ideal for boilerplate, branded letterheads, recurring document structures, or compliance-required templates. Supports the same formats as document upload: .docx, .pdf, .txt, .rtf, .md, .html, .htm. Max 50 MB validated; note the hosted transport layer caps any REQUEST BODY at ~32 MB and rejects larger ones at the gateway with an HTML (not JSON) error \u2014 base64 inflates content ~33%, so inline uploads are reliable up to ~20 MB of file bytes; beyond that use request_upload_url (pre-signed flow, up to 100 MB).", + "operationId": "upload_template_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "List your async chat jobs (in-progress, awaiting approval, completed, failed).", + "description": "Returns jobs sorted by most recent, optionally filtered by status. Use to monitor long-running AI edits started via chat_async, see which jobs are paused waiting for human approval, or audit completed work. Each job tracks the chat that started it, the changes made, and any HITL decisions logged.", + "operationId": "list_jobs", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter by job status", + "title": "Status" + }, + "description": "Filter by job status" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of jobs to return", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of jobs to return" + }, + { + "name": "compact", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs.", + "default": false, + "title": "Compact" + }, + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs/{job_id}": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "Get the status, partial results, and any pending changes for an async chat job.", + "description": "Returns the job's current state (pending, in_progress, awaiting_approval, completed, failed, or cancelled), intermediate AI responses streamed during execution, the final document HTML once complete, and any pending changes awaiting user approval (with chunk IDs and proposed HTML diffs in metadata.pending_changes). Poll this after chat_async to track progress and retrieve results. When status is awaiting_approval, call POST /v1/chat/{session_id}/approve to approve or deny each pending change. Headless/MCP clients get the same typed progress events the web SSE stream delivers via metadata.intermediate_responses (e.g. documents_changed, continue_prompt, model_fallback, proposed_change_batch); cross-session document updates from OTHER sessions are reported separately by polling GET /v1/sessions/{session_id}/doc-events.", + "operationId": "get_job", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Job Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs/{job_id}/cancel": { + "post": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "Cancel a pending or in-progress async chat job.", + "description": "Stops the AI mid-edit. Already-applied changes are preserved in the document; pending changes are discarded. Use to abort long-running operations that are no longer needed (e.g., user changed their mind, or you want to retry with different parameters or model_tier). Only jobs with status pending or processing can be cancelled. Returns the updated job details with status cancelled.", + "operationId": "cancel_job", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Job Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/jobs": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "List all async chat jobs for a specific session, most recent first.", + "description": "Returns the full job history of one document. Useful for auditing what the AI did to a document over time, or finding a specific job that's waiting for HITL approval. Same shape as list_jobs but scoped to one session.", + "operationId": "get_session_jobs", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of jobs to return", + "default": 20, + "title": "Limit" + }, + "description": "Maximum number of jobs to return" + }, + { + "name": "compact", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs.", + "default": false, + "title": "Compact" + }, + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/async": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Start a long-running or HITL-approved AI edit; returns a job_id to poll for results.", + "description": "Use instead of chat when (a) the edit is large or multi-step, (b) you need human approval on each proposed change before it applies (set approval_mode='ask_every_time'), or (c) you can't afford to block on a synchronous response. To replace the whole document with EXACT content you already hold, pass that content in document_html (a verbatim load \u2014 the AI never re-types it) or upload it via upload_document_base64; describing the content only in `message` makes the AI author it, which can drift from your text. Returns a job_id immediately. Poll get_job to track progress and retrieve the final document. Approve or deny pending changes via approve_change. Job state is durable and survives server restarts. Optional: model_tier (core/turbo/pro/max), thinking_depth (fast/balanced/deep), image_attachments for multimodal vision. RECOMMENDED for AI agents working with large documents (>20 pages): set response_mode='compact' so the job result skips the full HTML and surfaces only per-section diffs in chunk_diffs, saving thousands of tokens per poll. To read sections in compact mode, just send a natural-language chat request ('show me the pricing section') \u2014 the AI returns the content in the reply text. Always use natural language; the AI handles all internal section lookups. HITL workflow (approval_mode='ask_every_time'): 1) poll get_job until status=awaiting_approval, 2) read metadata.pending_changes for proposed edits, 3) call approve_change per change, 4) continue polling until status=completed.", + "operationId": "chat_async", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncChatRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncChatResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/stream": { + "get": { + "tags": [ + "v1", + "chat" + ], + "summary": "Stream Chat Progress", + "description": "Stream real-time progress for a chat job using Server-Sent Events (SSE).\n\nOpens an SSE connection that streams events as the AI processes your request.\nUse this with the job_id returned from POST /v1/chat/async.\n\nEvent types:\n- 'intermediate': Progress updates during processing (content, sequence, timestamp).\n- 'proposed_change_batch': The batch of document changes proposed for review, delivered as one\n event carrying changes[] (emitted when approval_mode is 'ask_every_time'). Changes are always\n delivered as a batch via this event.\n- 'document_sync': Chunk-id sync emitted before the agent runs, so changes can reference stable ids.\n- 'continue_prompt': A pause on a large edit, asking whether to continue or stop.\n- 'documents_changed': Signals that one or more documents were auto-applied (with per-document\n change counts and changed chunk ids).\n- 'model_fallback': Notice that the request automatically failed over to another model tier.\n- 'final': Processing complete. Contains the full result with AI response and document changes.\n- 'usage': Billing data emitted after 'final' (monthly_used, monthly_limit, monthly_remaining).\n- 'error': An error occurred (job failed, cancelled, or not found).\n\nAuthentication: Pass a token or api_key as a query parameter (required for EventSource which cannot set headers).", + "operationId": "stream_chat_progress_v1_chat__session_id__stream_get", + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "job_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "description": "Job ID to stream progress for", + "title": "Job Id" + }, + "description": "Job ID to stream progress for" + }, + { + "name": "last_sequence", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "SSE Last-Event-ID resume: only replay intermediate responses with sequence > this. Reconnects pass the highest sequence already processed so old events are NOT re-emitted; default 0 = full history.", + "default": 0, + "title": "Last Sequence" + }, + "description": "SSE Last-Event-ID resume: only replay intermediate responses with sequence > this. Reconnects pass the highest sequence already processed so old events are NOT re-emitted; default 0 = full history." + }, + { + "name": "token", + "in": "query", + "required": false, + "schema": { + "type": "string", + "description": "Authentication token (query parameter for EventSource compatibility)", + "title": "Token" + }, + "description": "Authentication token (query parameter for EventSource compatibility)" + }, + { + "name": "api_key", + "in": "query", + "required": false, + "schema": { + "type": "string", + "description": "API key (query parameter for EventSource compatibility)", + "title": "Api Key" + }, + "description": "API key (query parameter for EventSource compatibility)" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/approve": { + "post": { + "tags": [ + "v1", + "mcp", + "approval" + ], + "summary": "Approve or deny AI-proposed document changes one-by-one or in batch (HITL workflow).", + "description": "Used with chat_async when approval_mode='ask_every_time'. For each proposed change (with chunk_id and HTML diff), respond approved=true|false plus optional feedback for the AI to revise on. Approved changes apply atomically; denied changes are discarded; the AI may revise based on feedback in the next turn. Required for regulated workflows (legal, medical, compliance) where every AI edit must be reviewed before it touches the document. For single changes: set approved=true/false and optionally provide feedback. For batch decisions: provide a 'changes' array with per-change decisions. After approval, the job resumes processing and eventually reaches status=completed.", + "operationId": "approve_change", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApprovalRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/continue": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Resume or stop a chat turn paused by a large-edit continue prompt.", + "description": "Used with chat_async when a large edit paused to ask whether to keep going.\nPoll get_job until status=awaiting_approval AND metadata.awaiting_kind='continue_prompt';\nthen POST here with continue=true to resume (the AI picks up where it left off with a\nfresh time/step budget) or continue=false to stop (everything applied so far is kept).\nThe job then resumes/finishes and reaches status=completed. This is NOT the change-\napproval endpoint (that is approve_change) \u2014 it can only act on a continue-prompt pause.", + "operationId": "continue_chat", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContinueRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/poll/{session_id}": { + "get": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Poll Session Updates", + "description": "Long-poll for real-time job updates on a session (B2B organizations only).\n\nReturns immediately if there are recent job updates, or holds the connection\nopen up to the specified timeout. Use the 'since' parameter to avoid receiving\nduplicate updates.", + "operationId": "poll_session_updates_v1_poll__session_id__get", + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "since", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "ISO 8601 timestamp \u2014 only return updates after this time", + "title": "Since" + }, + "description": "ISO 8601 timestamp \u2014 only return updates after this time" + }, + { + "name": "timeout", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Long polling timeout in seconds (max 1800 = 30 minutes)", + "default": 1800, + "title": "Timeout" + }, + "description": "Long polling timeout in seconds (max 1800 = 30 minutes)" + }, + { + "name": "authorization", + "in": "header", + "required": true, + "schema": { + "type": "string", + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/upload": { + "post": { + "tags": [ + "v1", + "templates" + ], + "summary": "Upload User Template", + "description": "Upload a document file as a personal/organization template.\n\nThe file is processed synchronously: text extraction, HTML conversion, and content indexing.\nSupported formats: .docx, .pdf, .txt, .rtf, .md, .html, .htm\nTemplates are scoped to the uploading user or organization.", + "operationId": "upload_user_template", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_user_template" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates": { + "get": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "List all saved document templates available to the user or organization.", + "description": "Returns active (non-deleted) templates with name, format, size, and creation date metadata. Use to show the AI what reusable document structures are available for drafting new documents. Templates are scoped to the authenticated entity: users via the web app or sk_ key see only their own templates; organizations via lce_ key see theirs.", + "operationId": "list_user_templates", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/{template_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "Delete a saved document template by ID.", + "description": "Soft-deletes the template; only the owner (user or organization) can delete. Once deleted, the template no longer appears in list_user_templates and cannot be referenced by the AI for new documents. Existing documents already drafted from the template are unaffected.", + "operationId": "delete_user_template", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "template_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Template Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me": { + "get": { + "tags": [ + "users" + ], + "summary": "Get Current User Profile", + "description": "Get current user's profile information\n\nReturns user profile with subscription tier and usage limits", + "operationId": "get_current_user_profile_v1_users_me_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserProfileResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "patch": { + "tags": [ + "users" + ], + "summary": "Update User Profile", + "description": "Update current user's profile\n\nAllows updating display name, timezone, language, and preferences", + "operationId": "update_user_profile_v1_users_me_patch", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProfileRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/usage": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Usage Stats", + "description": "Get detailed usage statistics for current user\n\nReturns operation counts, token usage, and success rates by operation type", + "operationId": "get_user_usage_stats_v1_users_me_usage_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UsageStatsResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/sessions": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Sessions", + "description": "List all chat sessions for current user\n\nReturns list of sessions with last activity and message counts", + "operationId": "get_user_sessions_v1_users_me_sessions_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/UserSessionResponse" + }, + "type": "array", + "title": "Response Get User Sessions V1 Users Me Sessions Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/sessions/{session_id}": { + "delete": { + "tags": [ + "users" + ], + "summary": "Delete User Session", + "description": "Delete a specific chat session\n\nRemoves all messages for the given session ID (user must own the session)", + "operationId": "delete_user_session_v1_users_me_sessions__session_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/limits": { + "get": { + "tags": [ + "users" + ], + "summary": "Check User Limits", + "description": "Check current usage limits and remaining operations\n\nReturns real-time usage information with reset date", + "operationId": "check_user_limits_v1_users_me_limits_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/api-keys": { + "get": { + "tags": [ + "users" + ], + "summary": "List Api Keys", + "description": "List all API keys for the current user (masked).\n\nReturns key prefix and last 4 characters for identification.", + "operationId": "list_api_keys_v1_users_me_api_keys_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ApiKeyListItem" + }, + "type": "array", + "title": "Response List Api Keys V1 Users Me Api Keys Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "users" + ], + "summary": "Create Api Key", + "description": "Create a new API key for the current user.\n\nThe raw key is returned ONCE in the response. It cannot be retrieved again.", + "operationId": "create_api_key_v1_users_me_api_keys_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/api-keys/{key_id}": { + "delete": { + "tags": [ + "users" + ], + "summary": "Revoke Api Key", + "description": "Revoke (soft delete) an API key.\n\nThe key will be marked as inactive and can no longer be used for authentication.", + "operationId": "revoke_api_key_v1_users_me_api_keys__key_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "key_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Key Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/promo/redeem": { + "post": { + "tags": [ + "promo" + ], + "summary": "Redeem Promo", + "description": "Redeem a promo code and add the granted operations to the authenticated user's account.\n\nB2C only: web app login tokens and sk_ user API keys are accepted; lce_ org keys are rejected.\nRequires a verified email. Each user may redeem a given code at most once.\n\nRate-limited to 3 attempts per IP per hour. All attempts (success and failure) are\nlogged for audit.", + "operationId": "redeem_promo_v1_promo_redeem_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedeemRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedeemResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/promotions": { + "get": { + "tags": [ + "promo" + ], + "summary": "List My Promotions", + "description": "List the authenticated user's promotion redemptions, split into `active` (drawable now)\nand `history` (exhausted / expired / revoked).\n\nUsed by the Settings \u2192 Billing tab to render the Credits & Promotions section.", + "operationId": "list_my_promotions_v1_users_me_promotions_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPromotionsResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/uploads": { + "post": { + "tags": [ + "uploads", + "mcp", + "uploads" + ], + "summary": "Get a pre-signed URL to upload large files (.docx/PDF/HTML/MD/RTF) without bloating agent context.", + "description": "Returns a short-lived (5-minute) PUT URL plus a ready-to-run `curl_example`. An agent with shell access can run the `curl_example` directly to push the file to cloud storage, so the bytes never pass through its context window; clients without shell execution can surface the command or URL for the caller to run. After upload completes, call process_uploaded_document with the upload_id to trigger parsing. For files <100KB where token cost is trivial, upload_document_base64 still works inline. Max file size: 100 MB. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm, .tex (plus .zip LaTeX project archives).", + "operationId": "request_upload_url", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadUrlRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/uploads/{upload_id}/process": { + "post": { + "tags": [ + "uploads", + "mcp", + "uploads" + ], + "summary": "Parse an uploaded file into structured HTML with chunk IDs for targeted AI editing.", + "description": "Fetches the file uploaded via request_upload_url and runs the same parsing pipeline as upload_document_base64: every paragraph, heading, table, row, and cell gets a unique chunk ID, enabling targeted structural edits via chat (\"remove row 3 of the pricing table\" works), with tables, borders, shading, alternating row colors, fonts, and inline styling preserved on edit and export. By default the response is compact (metadata only, no document body) to keep your context small \u2014 the document is loaded into the session and you edit it via chat; pass return_html=true if you need the parsed HTML inline. Uploading and parsing is NOT itself a billable operation \u2014 you are charged only when the AI edits the document. Specify parse_mode='document' to load as the active editable document, or parse_mode='attachment' to load as a read-only AI-searchable reference.", + "operationId": "process_uploaded_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "upload_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The upload_id returned by request_upload_url.", + "title": "Upload Id" + }, + "description": "The upload_id returned by request_upload_url." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessUploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessDocumentResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/downloads": { + "post": { + "tags": [ + "uploads", + "mcp", + "downloads" + ], + "summary": "Get a pre-signed URL to download an exported document without proxying through the agent.", + "description": "Returns a short-lived (15-minute) GET URL plus a ready-to-run `curl_example`. An agent with shell access can run the `curl_example` directly to save the exported file to the working directory, so the bytes never pass through its context window; clients without shell execution (or callers who only want the link) can use the signed URL directly. Generates the document in the requested format and returns a time-limited signed download URL. Specify format as 'pdf', 'docx', 'html', 'markdown', or 'txt' (legacy 'doc' also accepted).", + "operationId": "request_download_url", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadUrlRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/challenge": { + "get": { + "tags": [ + "agents" + ], + "summary": "Agent Challenge", + "description": "Get a proof-of-work challenge for signup (only needed when the challenge gate is enabled).\n\nSolve it headlessly with no browser: brute-force an integer `n` in\n[0, maxNumber] such that `SHA-256(salt + str(n))` equals `challenge`, then\nbase64-encode `{algorithm, challenge, number:n, salt, signature}` and send it\nas the `altcha` field in POST /v1/agents/signup.", + "operationId": "agent_challenge_v1_agents_challenge_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/v1/agents/signup": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Signup", + "description": "Create a SuperDocs account and get a working API key in one call \u2014 no human required.\n\nYou (an AI agent) get a normal **free account: 500 operations/month**, exactly\nlike a human. Use the returned `sk_` key as `Authorization: Bearer ` on\nthis API or the MCP server at `/mcp`. Store the key \u2014 it is shown only once.\nSave the whole response to `~/.superdocs/agent_credentials.json` and call\n`GET /v1/agents/whoami` before signing up again so you reuse the same account.\nWhen you approach the 500-op cap, hand the account to your human\n(POST /v1/agents/handoff) so they can adopt it and pay to continue. If you\nare acting for a user, confirm with them before accepting the terms.", + "operationId": "agent_signup_v1_agents_signup_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentSignupRequest" + }, + { + "type": "null" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentSignupResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/whoami": { + "get": { + "tags": [ + "agents", + "mcp" + ], + "summary": "Check your account status: subscription tier plus monthly operations used and remaining.", + "description": "Check your SuperDocs account status: tier, monthly operations used/remaining, and (for agent accounts) whether a human has adopted it.\n\nCall this with your `sk_` key. Useful before doing work (to confirm you have\noperations left) and before signing up again (to reuse your account).\n\nNearing your monthly cap and need to keep going? Hand the account to a human:\nPOST /v1/agents/handoff with their email returns a one-time takeover code to\ngive them; they open the emailed link, enter the code, and adopt the account\nin place (your work and this API key keep working) so they can upgrade and\npay. No human? POST /v1/agents/request-upgrade instead. Full flow:\nhttps://docs.superdocs.app/introduction/agent-signup", + "operationId": "get_account_status", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentWhoamiResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/handoff": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Handoff", + "description": "Email your human a one-time link to ADOPT this account (take ownership + pay).\n\nCall with your `sk_` key. Pass `working_context` (e.g. \"the ~/Documents/acme\nproject on your Mac\") so the email is recognizable and not mistaken for spam.\n\nThe response includes a short **takeover_code** (like ABCD-1234). You MUST show\nthis code to your human operator \u2014 they enter it after opening the emailed link\nand signing in. It is the security check that stops anyone who merely received\nthe email (a wrong address, or a prompt-injected one) from taking the account.\nTell your operator the code directly (you may also save it locally, e.g.\n~/.superdocs/takeover-code.txt); never email it or post it anywhere external.\n\nThe human opens the link, signs in, enters the code, and the account becomes\ntheirs in place \u2014 you keep all your work and this API key keeps working.", + "operationId": "agent_handoff_v1_agents_handoff_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentHandoffRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/adopt": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Adopt", + "description": "Human takes over an agent account IN PLACE. Auth = the human's signed-in identity token.\n\nCalled by the web app's adopt page after the human signs in. Verifies the\nhuman's signed-in identity (WITHOUT the get-or-create middleware, to avoid\nminting a duplicate row), then re-points the agent account row to that\nidentity \u2014 `users.id` is preserved, so the agent's documents, usage, and API\nkey all stay. If the human already has an account, returns 409.", + "operationId": "agent_adopt_v1_agents_adopt_post", + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentAdoptRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/adopt-info": { + "get": { + "tags": [ + "agents" + ], + "summary": "Agent Adopt Info", + "description": "Public, token-keyed context for the adopt page: WHICH agent set this account up,\nwhere it runs, and where to find the takeover code. Recognition / anti-phishing only.\nNEVER returns the code itself; the unguessable token is the gate.", + "operationId": "agent_adopt_info_v1_agents_adopt_info_get", + "parameters": [ + { + "name": "token", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Token" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/agents/request-upgrade": { + "post": { + "tags": [ + "agents" + ], + "summary": "Agent Request Upgrade", + "description": "For a FULLY AUTONOMOUS agent with no human who can upgrade: register interest in paying directly.\n\nWe don't offer agent-native (no-human) payment yet, but we're gauging demand.\nCall this with your `sk_` key when you've hit your cap and genuinely cannot\ninvolve a human. We record it and follow up (or reach hello@superdocs.app).\nIf a human CAN help, use POST /v1/agents/handoff instead \u2014 that upgrades you now.", + "operationId": "agent_request_upgrade_v1_agents_request_upgrade_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentUpgradeRequest" + }, + { + "type": "null" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/": { + "get": { + "summary": "Root", + "operationId": "root__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health": { + "get": { + "tags": [ + "mcp" + ], + "summary": "Verify the SuperDocs MCP server is reachable and serving traffic.", + "description": "Returns 200 with {\"status\":\"healthy\"} when the API is up. Call this once after MCP install to confirm the connection works before invoking other tools. No authentication required.", + "operationId": "health", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health/deep": { + "get": { + "summary": "Health Check Deep", + "description": "Deep health check that verifies the job queue, AI workflow state store, and database are all responsive within a bounded time budget. Used by container orchestrators as a liveness probe to distinguish \"process alive and TCP listening\" (what /health reports) from \"application is actually serving requests\" (this endpoint).", + "operationId": "health_check_deep_health_deep_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/oauth-protected-resource": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource_mcp__well_known_oauth_protected_resource_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-protected-resource/mcp": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource__well_known_oauth_protected_resource_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-protected-resource": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource__well_known_oauth_protected_resource_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/oauth-authorization-server": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server_mcp__well_known_oauth_authorization_server_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-authorization-server/mcp": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server__well_known_oauth_authorization_server_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-authorization-server": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server__well_known_oauth_authorization_server_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/openid-configuration": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration_mcp__well_known_openid_configuration_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/openid-configuration/mcp": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration__well_known_openid_configuration_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/openid-configuration": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration__well_known_openid_configuration_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/mcp.json": { + "get": { + "summary": "Mcp Server Card", + "operationId": "mcp_server_card__well_known_mcp_json_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/mcp-server-card": { + "get": { + "summary": "Mcp Server Card", + "operationId": "mcp_server_card__well_known_mcp_server_card_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/api-catalog": { + "get": { + "summary": "Api Catalog", + "operationId": "api_catalog__well_known_api_catalog_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ActivePromotionInfo": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id", + "description": "Unique identifier for this promotion grant." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Human-friendly promotion name (e.g., 'YC SUS India 2026 cohort')." + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining", + "description": "Operations still available in this promotion bucket." + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted", + "description": "Total operations originally granted by this redemption." + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At", + "description": "ISO 8601 expiry timestamp. Null if the credits never expire." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_remaining", + "ops_granted" + ], + "title": "ActivePromotionInfo", + "description": "A single active (drawable) promotion belonging to the authenticated user." + }, + "ActivePromotionOut": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + } + }, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at" + ], + "title": "ActivePromotionOut", + "description": "A single active (drawable) promotion grant belonging to the current user." + }, + "AgentAdoptRequest": { + "properties": { + "token": { + "type": "string", + "maxLength": 128, + "title": "Token", + "description": "The handoff token from the adopt link." + }, + "code": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Code", + "description": "The one-time takeover code your AI agent shows you (8 characters like ABCD-1234). Required for accounts handed off with a code." + } + }, + "type": "object", + "required": [ + "token" + ], + "title": "AgentAdoptRequest" + }, + "AgentHandoffRequest": { + "properties": { + "email": { + "type": "string", + "maxLength": 255, + "title": "Email", + "description": "Your human operator's email. A one-time link to adopt this account is sent there." + }, + "working_context": { + "anyOf": [ + { + "type": "string", + "maxLength": 300 + }, + { + "type": "null" + } + ], + "title": "Working Context", + "description": "Recommended: a short, human-readable description of where you are running, so your operator recognizes the email and it isn't mistaken for spam (e.g. 'the ~/Documents/acme project on your Mac', or 'the Acme Slack workspace')." + }, + "code_location": { + "anyOf": [ + { + "type": "string", + "maxLength": 300 + }, + { + "type": "null" + } + ], + "title": "Code Location", + "description": "Optional: where you show or save the takeover code so your operator can find it (e.g. 'this chat', or '~/.superdocs/takeover-code.txt')." + } + }, + "type": "object", + "required": [ + "email" + ], + "title": "AgentHandoffRequest" + }, + "AgentQuota": { + "properties": { + "tier": { + "type": "string", + "title": "Tier" + }, + "monthly_limit": { + "type": "integer", + "title": "Monthly Limit" + }, + "used": { + "type": "integer", + "title": "Used" + }, + "remaining": { + "type": "integer", + "title": "Remaining" + }, + "resets_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Resets At" + } + }, + "type": "object", + "required": [ + "tier", + "monthly_limit", + "used", + "remaining" + ], + "title": "AgentQuota" + }, + "AgentSignupRequest": { + "properties": { + "terms_accepted": { + "type": "boolean", + "title": "Terms Accepted", + "description": "Must be true. Accepts the Terms at https://superdocs.app/terms. If you are an agent, confirm with your user first.", + "default": false + }, + "agent_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Agent Name", + "description": "Optional label for this agent account." + }, + "operated_by_email": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Operated By Email", + "description": "Optional. Your human operator's email (only used if you later hand off the account)." + }, + "model_metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Model Metadata", + "description": "Optional. Free-form metadata about the agent/model (forensics; capped at 10KB)." + }, + "altcha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Altcha", + "description": "Only required if the proof-of-work challenge is enabled: the base64 solution from GET /v1/agents/challenge." + } + }, + "type": "object", + "title": "AgentSignupRequest", + "description": "Body for POST /v1/agents/signup. Every field except terms_accepted is optional." + }, + "AgentSignupResponse": { + "properties": { + "account_id": { + "type": "string", + "title": "Account Id" + }, + "slug": { + "type": "string", + "title": "Slug" + }, + "email": { + "type": "string", + "title": "Email" + }, + "api_key": { + "type": "string", + "title": "Api Key", + "description": "Your API key (sk_...). Shown ONCE. Send it as `Authorization: Bearer `. Store it now." + }, + "quota": { + "$ref": "#/components/schemas/AgentQuota" + }, + "endpoints": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Endpoints" + }, + "mcp_setup": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Mcp Setup", + "description": "Copy-paste commands to connect the SuperDocs MCP server to your client, plus the REST fallback." + }, + "handoff": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Handoff" + }, + "important": { + "type": "string", + "title": "Important" + } + }, + "type": "object", + "required": [ + "account_id", + "slug", + "email", + "api_key", + "quota", + "endpoints", + "mcp_setup", + "handoff", + "important" + ], + "title": "AgentSignupResponse" + }, + "AgentUpgradeRequest": { + "properties": { + "note": { + "anyOf": [ + { + "type": "string", + "maxLength": 1000 + }, + { + "type": "null" + } + ], + "title": "Note", + "description": "Optional: your use case, or why you can't involve a human." + } + }, + "type": "object", + "title": "AgentUpgradeRequest" + }, + "AgentWhoamiResponse": { + "properties": { + "account_id": { + "type": "string", + "title": "Account Id" + }, + "tier": { + "type": "string", + "title": "Tier" + }, + "quota": { + "$ref": "#/components/schemas/AgentQuota" + }, + "is_agent_account": { + "type": "boolean", + "title": "Is Agent Account" + }, + "adopted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Adopted", + "description": "For agent accounts: whether a human has adopted it yet. Null for regular accounts." + }, + "hint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Hint" + } + }, + "type": "object", + "required": [ + "account_id", + "tier", + "quota", + "is_agent_account" + ], + "title": "AgentWhoamiResponse" + }, + "ApiKeyListItem": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Key identifier (UUID)." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Label assigned to this key." + }, + "key_prefix": { + "type": "string", + "title": "Key Prefix", + "description": "First 7 characters of the key (e.g., 'sk_a1b2')." + }, + "last_four": { + "type": "string", + "title": "Last Four", + "description": "Last 4 characters of the key for identification." + }, + "is_active": { + "type": "boolean", + "title": "Is Active", + "description": "Whether the key is active. Revoked keys show as false." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the key was created." + }, + "last_used_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Used At", + "description": "ISO 8601 timestamp of last use, or null if never used." + } + }, + "type": "object", + "required": [ + "id", + "name", + "key_prefix", + "last_four", + "is_active", + "created_at" + ], + "title": "ApiKeyListItem", + "description": "API key summary (masked for security)." + }, + "ApprovalRequest": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job ID that is awaiting approval." + }, + "change_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Change Id", + "description": "Specific change to approve/deny (for individual decisions). Get change IDs from the job's pending_changes." + }, + "approved": { + "type": "boolean", + "title": "Approved", + "description": "Whether to approve (true) or deny (false) the change." + }, + "feedback": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Feedback", + "description": "Optional feedback explaining why a change was denied. Helps the AI adjust future suggestions." + }, + "changes": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Changes", + "description": "Batch decisions: array of {change_id, approved, feedback} objects. Use for 'Accept All' or 'Deny All'." + } + }, + "type": "object", + "required": [ + "job_id", + "approved" + ], + "title": "ApprovalRequest", + "description": "Approve or deny proposed AI document changes." + }, + "AsyncChatRequest": { + "properties": { + "message": { + "type": "string", + "maxLength": 100000, + "title": "Message", + "description": "Your message to the AI assistant." + }, + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Unique session identifier. Reuse to continue a conversation." + }, + "document_html": { + "anyOf": [ + { + "type": "string", + "maxLength": 50000000 + }, + { + "type": "null" + } + ], + "title": "Document Html", + "description": "Current document HTML. OMIT this when the session already holds the document \u2014 the server persists it across turns, so you don't re-send it every turn; pass it only to load new content or replace the document wholesale (a verbatim load \u2014 the AI never re-types content passed here). Include data-chunk-id attributes on elements to enable targeted AI edits." + }, + "user_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User identifier. Automatically set from authentication \u2014 typically omit this." + }, + "image_attachments": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ImageAttachmentData" + }, + "type": "array", + "maxItems": 20 + }, + { + "type": "null" + } + ], + "title": "Image Attachments", + "description": "Inline images for vision-based analysis (base64-encoded)." + }, + "async_mode": { + "type": "boolean", + "title": "Async Mode", + "description": "Must be true for async processing. Included for backward compatibility.", + "default": true + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier", + "description": "AI model to use: 'core' (default, fast), 'turbo' (fastest), 'pro' (advanced reasoning), 'max' (most capable)." + }, + "thinking_depth": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Thinking Depth", + "description": "Reasoning depth: 'fast', 'balanced' (default), or 'deep'. Controls how much analysis the AI performs." + }, + "approval_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Approval Mode", + "description": "Change review mode: 'approve_all' (default, auto-applies changes) or 'ask_every_time' (pauses for your review)." + }, + "response_mode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "full", + "compact" + ] + }, + { + "type": "null" + } + ], + "title": "Response Mode", + "description": "Response shape control. 'full' (default) puts the complete updated document HTML in the job result \u2014 required by web app editors and recommended for small documents. 'compact' (recommended for AI agents editing large documents) suppresses the full HTML and stores only per-section diffs (chunk_diffs), saving thousands of tokens when polling get_job. To read sections in compact mode, send a natural-language request to chat ('show me the pricing section') \u2014 the AI returns the content in the reply text.", + "default": "full" + }, + "cursor_context": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Cursor Context", + "description": "Editor cursor context at message time, used to default the insert location for new images, diagrams, or sections when the user's prompt doesn't name a position. Shape: {chunk_id: str, pos_in_chunk?: int, text_before?: str, text_after?: str}. Omit when the user typed in chat without focusing the editor first." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Target a specific open document by id (ids come from /v1/sessions/{session_id}/documents). Omit to target the focused document (default \u2014 unchanged single-document behaviour). When set, that document becomes the focus for this turn." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window (set by the web app) so concurrent edits from two windows of the same chat are merged rather than overwritten. Agents/API may omit." + }, + "cross_session_memory": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory", + "description": "When true, the AI carries a private rolling memory of context across THIS account's chats (off by default). Per-request override of your saved Settings default." + }, + "cross_session_search": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Search", + "description": "When true, the AI may search your PAST chats and documents on demand to reuse prior work and open a found document (off by default). Per-request override of your saved Settings default; never affects the always-on search of the open document." + }, + "cross_session_memory_key": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory Key", + "description": "Optional per-request id of YOUR end-customer so the cross-session memory note is kept in a separate file per end-customer. Omit for one account-level note (the default / B2C)." + }, + "cross_session_scope": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 2000 + }, + { + "type": "null" + } + ], + "title": "Cross Session Scope", + "description": "Optional list of session ids to restrict cross-session SEARCH to (e.g. one end-customer's sessions). Omit to search all of this account's sessions. Only narrows within the API-key owner; ignored unless cross_session_search is on." + }, + "touched_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Touched Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) the user actually edited since the last sync, sent alongside document_html. When present, a chunk NOT in this list whose text is unchanged keeps its stored formatting byte-for-byte even if the client serialized it differently (protects styling from editor round-trip loss). Chunks in the list \u2014 and any chunk whose text changed \u2014 always take the submitted content. Omit for the default behavior (any differing chunk is treated as an edit)." + }, + "deleted_part_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Deleted Part Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) of OUT-OF-FLOW part sections (page headers/footers, footnote/endnote bodies, comments) to delete explicitly, sent alongside document_html. Out-of-flow parts absent from document_html are always KEPT \u2014 an editor view not containing them is their normal state, never a deletion \u2014 so removing one requires naming its id here. Ids that are not stored out-of-flow parts are ignored (in-flow content keeps the default behavior). Omit when deleting nothing." + } + }, + "type": "object", + "required": [ + "message", + "session_id" + ], + "title": "AsyncChatRequest", + "description": "Start an async chat request that processes in the background." + }, + "AsyncChatResponse": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job identifier. Poll GET /v1/jobs/{job_id} for status and results." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier for this conversation." + }, + "status": { + "type": "string", + "title": "Status", + "description": "Initial job status (always 'pending')." + }, + "message": { + "type": "string", + "title": "Message", + "description": "Human-readable status message.", + "default": "Chat request queued for processing" + } + }, + "type": "object", + "required": [ + "job_id", + "session_id", + "status" + ], + "title": "AsyncChatResponse", + "description": "Response from starting an async chat request." + }, + "Base64UploadRequest": { + "properties": { + "filename": { + "type": "string", + "maxLength": 512, + "title": "Filename", + "description": "Original filename with extension (e.g. 'contract.pdf'). Used for file type detection." + }, + "file_base64": { + "type": "string", + "maxLength": 50000000, + "title": "File Base64", + "description": "Base64-encoded file content." + }, + "session_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session ID. Auto-generated if not provided." + }, + "return_html": { + "type": "boolean", + "title": "Return Html", + "description": "With a session_id: false (default) returns compact metadata only (chunks_count, version_id, page_setup) \u2014 the document is loaded into the session and you edit it via chat, keeping your context small; true returns the full parsed HTML inline. Ignored on the convert-only path (no session_id), which always returns the converted html.", + "default": false + } + }, + "type": "object", + "required": [ + "filename", + "file_base64" + ], + "title": "Base64UploadRequest", + "description": "JSON request body for base64-encoded file uploads." + }, + "Body_upload_attachment": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id" + }, + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "session_id", + "file" + ], + "title": "Body_upload_attachment" + }, + "Body_upload_document_to_editor": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id" + }, + "open_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Open Mode", + "default": "replace" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_document_to_editor" + }, + "Body_upload_inline_image": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_inline_image" + }, + "Body_upload_user_template": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_user_template" + }, + "ChatResponse": { + "properties": { + "response": { + "type": "string", + "title": "Response", + "description": "AI assistant's response text." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier for this conversation." + }, + "document_changes": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentChanges" + }, + { + "type": "null" + } + ], + "description": "Document modifications made by the AI. Present only when the document was changed." + }, + "usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/UsageInfo" + }, + { + "type": "null" + } + ], + "description": "Operation usage data. Present for authenticated users with usage tracking." + }, + "hint": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Hint", + "description": "Advisory guidance for API callers (additive; absent on most responses). Currently: prefer_chat_async_for_large_generations \u2014 this turn ran long enough that the same work submitted via POST /v1/chat/async would be safer (no gateway timeout risk) and reports progress." + } + }, + "type": "object", + "required": [ + "response", + "session_id" + ], + "title": "ChatResponse", + "description": "AI response with optional document changes and usage data.", + "examples": [ + { + "document_changes": { + "changes_summary": "Document updated by AI", + "updated_html": "

Section 3

Updated content...

", + "version_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7" + }, + "response": "I've added a confidentiality clause to section 3 covering non-disclosure obligations.", + "session_id": "session_abc123", + "usage": { + "monthly_limit": 500, + "monthly_remaining": 458, + "monthly_used": 42, + "subscription_tier": "free", + "was_billable": true + } + } + ] + }, + "ContinueRequest": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job ID awaiting a continue decision." + }, + "continue": { + "type": "boolean", + "title": "Continue", + "description": "True to resume with a fresh budget; false to stop here (work so far is kept)." + } + }, + "type": "object", + "required": [ + "job_id", + "continue" + ], + "title": "ContinueRequest", + "description": "Resume or stop a chat turn that paused to ask whether to continue." + }, + "CreateApiKeyRequest": { + "properties": { + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1, + "title": "Name", + "description": "A label for this key (e.g., 'My App', 'CI Pipeline')." + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "CreateApiKeyRequest", + "description": "Create a new API key." + }, + "CreateApiKeyResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Key identifier (UUID). Use this to revoke the key later." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Label you assigned to this key." + }, + "key": { + "type": "string", + "title": "Key", + "description": "The full API key (sk_... format). Copy this now \u2014 it cannot be retrieved again." + }, + "key_prefix": { + "type": "string", + "title": "Key Prefix", + "description": "First 7 characters of the key (e.g., 'sk_a1b2') for identification." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the key was created." + } + }, + "type": "object", + "required": [ + "id", + "name", + "key", + "key_prefix", + "created_at" + ], + "title": "CreateApiKeyResponse", + "description": "Newly created API key. The raw key is shown only once \u2014 save it immediately." + }, + "DocumentChanges": { + "properties": { + "updated_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Updated Html", + "description": "Full updated document HTML with data-chunk-id attributes. Apply this to your editor to sync the document. Present in 'full' response mode (default); null in 'compact' mode." + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id", + "description": "Document version identifier. Changes on every modification." + }, + "changes_summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Changes Summary", + "description": "Human-readable summary of what was changed." + }, + "requires_approval": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Requires Approval", + "description": "If true, changes need review via the approve endpoint before being applied." + }, + "pending_changes": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pending Changes", + "description": "Proposed changes awaiting approval. Present when requires_approval is true." + }, + "changes": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Changes", + "description": "History of individual changes applied during this request." + }, + "chunk_diffs": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Chunk Diffs", + "description": "Per-section before/after content for changes that were applied this turn. Present in 'compact' response mode (when updated_html is null) so the agent can verify what was modified without paying token cost for the full document. Same shape as pending_changes." + } + }, + "additionalProperties": true, + "type": "object", + "title": "DocumentChanges", + "description": "Document modifications produced by the AI assistant." + }, + "DocumentState": { + "properties": { + "html_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Html Content", + "description": "Full document HTML content with data-chunk-id attributes." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Session-local id (slug) of the focused document. Identifies the document on restore so a restored tab stays in sync with edits made to it in other sessions." + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id", + "description": "Document version identifier (UUID string)." + }, + "chunk_count": { + "type": "integer", + "title": "Chunk Count", + "description": "Number of content sections in the document.", + "default": 0 + }, + "last_modified": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Modified", + "description": "ISO 8601 timestamp of the last modification." + }, + "attachments": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Attachments", + "description": "Session attachments. Each item has: id (string), filename (string), file_extension (string), processing_status ('ready', 'processing', or 'failed')." + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup", + "description": "Source page geometry of the focused document (width_in, height_in, margin_in{top,right,bottom,left}, orientation) when known, else null. The editor renders the page at this geometry on restore; null \u21d2 US-Letter + 1in default." + } + }, + "type": "object", + "title": "DocumentState", + "description": "Current document state for session restoration." + }, + "DownloadUrlRequest": { + "properties": { + "session_id": { + "type": "string", + "maxLength": 256, + "title": "Session Id", + "description": "Session ID whose current document should be exported and packaged for download." + }, + "format": { + "type": "string", + "enum": [ + "docx", + "pdf", + "html", + "markdown", + "txt", + "doc" + ], + "title": "Format", + "description": "Export format (docx, pdf, html, markdown, txt).", + "default": "docx" + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Optional Content-Disposition filename for the download (without extension). Auto-detected from the document's first heading if not provided." + }, + "options": { + "$ref": "#/components/schemas/ExportOptions", + "description": "Customization (paper, margins, filename, watermark, etc.). Mirrors ExportRequest.options." + } + }, + "type": "object", + "required": [ + "session_id" + ], + "title": "DownloadUrlRequest" + }, + "DownloadUrlResponse": { + "properties": { + "download_url": { + "type": "string", + "title": "Download Url" + }, + "expires_at": { + "type": "string", + "title": "Expires At" + }, + "expires_in_seconds": { + "type": "integer", + "title": "Expires In Seconds" + }, + "curl_example": { + "type": "string", + "title": "Curl Example" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "format": { + "type": "string", + "title": "Format" + } + }, + "type": "object", + "required": [ + "download_url", + "expires_at", + "expires_in_seconds", + "curl_example", + "filename", + "format" + ], + "title": "DownloadUrlResponse" + }, + "EnhancedChatHistoryResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier." + }, + "messages": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Messages", + "description": "Ordered list of messages. Each item has: id (string), sender ('user' or 'ai'), content (string), timestamp (ISO 8601), turn_index (int), checkpoint_id (string or null \u2014 present on turns recorded after the revertability feature shipped; null for legacy turns where revert is unavailable)." + }, + "document_state": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentState" + }, + { + "type": "null" + } + ], + "description": "Current document state. Present if the session has an active document." + }, + "editor_action": { + "type": "string", + "title": "Editor Action", + "description": "Client instruction: 'update' (load document_state HTML into editor), 'clear' (reset editor), or 'keep' (no change).", + "default": "keep" + } + }, + "type": "object", + "required": [ + "session_id", + "messages" + ], + "title": "EnhancedChatHistoryResponse", + "description": "Conversation history with document state and restoration instructions." + }, + "ExportOptions": { + "properties": { + "paper_size": { + "type": "string", + "enum": [ + "A4", + "Letter", + "A3", + "Legal" + ], + "title": "Paper Size", + "description": "Page size for DOCX/PDF/legacy .doc. HTML/MD/TXT exports ignore.", + "default": "Letter" + }, + "orientation": { + "type": "string", + "enum": [ + "portrait", + "landscape" + ], + "title": "Orientation", + "description": "Page orientation for DOCX/PDF/legacy .doc.", + "default": "portrait" + }, + "margins": { + "type": "string", + "enum": [ + "narrow", + "normal", + "wide", + "custom" + ], + "title": "Margins", + "description": "Page margins preset. 'narrow' = 0.5in, 'normal' = 1.0in, 'wide' = 1.5in. 'custom' uses custom_margins_inches.", + "default": "normal" + }, + "custom_margins_inches": { + "anyOf": [ + { + "additionalProperties": { + "type": "number" + }, + "propertyNames": { + "enum": [ + "top", + "right", + "bottom", + "left" + ] + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Margins Inches", + "description": "Required when margins='custom'. Each value 0.25-3.0 inches." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Custom filename (no extension). Auto-detected from first

if unset." + }, + "embed_images": { + "type": "boolean", + "title": "Embed Images", + "description": "HTML export only. When True, images are base64-embedded for offline portability (raises size cap to 150 MB). When False, images are referenced by URL (smaller file, online-only).", + "default": false + }, + "watermark_text": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Watermark Text", + "description": "PDF only. Optional text watermark overlaid on every page." + }, + "watermark_opacity": { + "type": "number", + "maximum": 1.0, + "minimum": 0.05, + "title": "Watermark Opacity", + "description": "PDF watermark opacity, 0.05-1.0.", + "default": 0.3 + }, + "fidelity": { + "anyOf": [ + { + "type": "string", + "enum": [ + "strict", + "compat" + ] + }, + { + "type": "null" + } + ], + "title": "Fidelity", + "description": "Export fidelity. 'strict' (server default) renders only the formatting the document carries \u2014 no imposed table widths/borders (except visual defaults on fully unstyled tables), overflow-only clamping, highlight colors honored. 'compat' reproduces the legacy normalized output." + } + }, + "type": "object", + "title": "ExportOptions", + "description": "User-facing export customisation (page size, orientation, margins,\nfilename, watermark). Sent on every export request." + }, + "ExportRequest": { + "properties": { + "session_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session ID to export from (the document is taken from the session). Required if html is omitted." + }, + "html": { + "anyOf": [ + { + "type": "string", + "maxLength": 200000000 + }, + { + "type": "null" + } + ], + "title": "Html", + "description": "HTML content to export inline. Used instead of the session document if provided." + }, + "format": { + "type": "string", + "enum": [ + "docx", + "pdf", + "html", + "markdown", + "txt", + "doc" + ], + "title": "Format", + "description": "Export format. One of docx (default), pdf, html, markdown, txt.", + "default": "docx" + }, + "options": { + "$ref": "#/components/schemas/ExportOptions", + "description": "Customization (paper, margins, filename, watermark, etc.)." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "DEPRECATED. Use options.filename. Top-level kept for legacy callers." + }, + "source_filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Source Filename", + "description": "The document's original/source filename (e.g. the name it was uploaded under). Used as the default export filename when no explicit options.filename is set, taking precedence over the first heading." + }, + "upload_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Upload Id", + "description": "ID returned by /v1/uploads (large-document upload flow, for payloads above ~25 MB). When set, the route fetches the HTML body from the uploaded payload instead of using the html field." + } + }, + "type": "object", + "title": "ExportRequest", + "description": "Unified request body for POST /v1/documents/export.\n\nBackward-compat notes:\n- Top-level ``filename`` is retained for legacy clients that sent only\n ``{ html }`` and relied on the filename being auto-extracted from the\n first

. New clients should use ``options.filename`` \u2014\n options.filename wins when both are set.\n- ``format`` defaults to \"docx\". Legacy callers passing format=\"doc\"\n explicitly keep working during a short back-compat window." + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "HistoricalPromotionOut": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + }, + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at", + "status" + ], + "title": "HistoricalPromotionOut", + "description": "An exhausted, expired, or revoked promotion grant kept for the user's history view." + }, + "ImageAttachmentData": { + "properties": { + "id": { + "type": "string", + "maxLength": 256, + "title": "Id", + "description": "Unique identifier for this image." + }, + "name": { + "type": "string", + "maxLength": 512, + "title": "Name", + "description": "Original filename of the image." + }, + "base64Data": { + "type": "string", + "maxLength": 50000000, + "title": "Base64Data", + "description": "Base64-encoded image data." + }, + "mimeType": { + "type": "string", + "maxLength": 128, + "title": "Mimetype", + "description": "MIME type (e.g., 'image/png', 'image/jpeg')." + }, + "size": { + "type": "integer", + "maximum": 50000000.0, + "title": "Size", + "description": "File size in bytes." + } + }, + "type": "object", + "required": [ + "id", + "name", + "base64Data", + "mimeType", + "size" + ], + "title": "ImageAttachmentData", + "description": "Inline image attachment for vision-based analysis." + }, + "InitSessionRequest": { + "properties": { + "document_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Document Ids", + "description": "Durable document ids (from GET /v1/documents) to open into the brand-new session. Omit/empty to start an empty session." + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Optional client-chosen session id (the web UI passes its own so its session model stays consistent). Omit and the server mints one \u2014 the one-call way for an MCP/API integrator to start a session with documents already open." + } + }, + "type": "object", + "title": "InitSessionRequest" + }, + "JobListResponse": { + "properties": { + "jobs": { + "items": { + "$ref": "#/components/schemas/JobResponse" + }, + "type": "array", + "title": "Jobs", + "description": "Array of job details." + }, + "total": { + "type": "integer", + "title": "Total", + "description": "Total number of jobs returned." + } + }, + "type": "object", + "required": [ + "jobs", + "total" + ], + "title": "JobListResponse", + "description": "List of async jobs." + }, + "JobMetadata": { + "properties": { + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Uploaded filename (attachment processing jobs)." + }, + "file_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "File Size", + "description": "File size in bytes (attachment processing jobs)." + }, + "content_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content Type", + "description": "MIME type of uploaded file (attachment processing jobs)." + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Message", + "description": "Original user message (chat jobs)." + }, + "document_html_provided": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Document Html Provided", + "description": "Whether document HTML was included in the request (chat jobs)." + }, + "pending_changes": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pending Changes", + "description": "Proposed changes awaiting approval. Present when job status is 'awaiting_approval'." + }, + "intermediate_responses": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Intermediate Responses", + "description": "Progress updates during processing. Each item has: type, content, sequence, timestamp." + } + }, + "additionalProperties": true, + "type": "object", + "title": "JobMetadata", + "description": "Job metadata. Contents vary by job type." + }, + "JobResponse": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Unique job identifier. Use this to poll for status updates." + }, + "organization_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Organization Id", + "description": "Organization that owns this job (null for user jobs)." + }, + "user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User that owns this job (null for organization jobs)." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session associated with this job." + }, + "job_type": { + "type": "string", + "title": "Job Type", + "description": "Type of job: 'chat' or 'attachment_processing'." + }, + "status": { + "type": "string", + "title": "Status", + "description": "Current status: 'pending', 'in_progress', 'awaiting_approval', 'completed', 'failed', or 'cancelled'." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the job was created." + }, + "updated_at": { + "type": "string", + "title": "Updated At", + "description": "ISO 8601 timestamp of the last status change." + }, + "progress": { + "type": "integer", + "title": "Progress", + "description": "Progress percentage (0-100)." + }, + "result": { + "anyOf": [ + { + "$ref": "#/components/schemas/JobResult" + }, + { + "type": "null" + } + ], + "description": "Job output. Present when status is 'completed'. Contains AI response and document changes for chat jobs." + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error", + "description": "Error message. Present when status is 'failed'." + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/JobMetadata" + }, + { + "type": "null" + } + ], + "description": "Job metadata and context. Contains pending_changes when status is 'awaiting_approval'." + } + }, + "type": "object", + "required": [ + "job_id", + "session_id", + "job_type", + "status", + "created_at", + "updated_at", + "progress" + ], + "title": "JobResponse", + "description": "Status and details of an async job." + }, + "JobResult": { + "properties": { + "response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Response", + "description": "AI response text (chat jobs)." + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session identifier (chat jobs)." + }, + "document_changes": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentChanges" + }, + { + "type": "null" + } + ], + "description": "Document modifications (chat jobs)." + }, + "usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/UsageInfo" + }, + { + "type": "null" + } + ], + "description": "Usage tracking data (chat jobs)." + }, + "attachment_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Attachment Id", + "description": "Processed attachment identifier (attachment processing jobs)." + } + }, + "additionalProperties": true, + "type": "object", + "title": "JobResult", + "description": "Job result data. Structure depends on job type." + }, + "LargeExportEmailRequest": { + "properties": { + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id" + }, + "format": { + "type": "string", + "title": "Format", + "default": "docx" + }, + "options": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Options" + }, + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filename" + }, + "source_filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Filename" + }, + "recipient_email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Recipient Email" + } + }, + "type": "object", + "title": "LargeExportEmailRequest", + "description": "Body for POST /v1/documents/export/email-request.\n\nUsed when a synchronous export exceeds the inline body cap. Send the same\npayload you would POST to /v1/documents/export (minus the html field \u2014\nthat comes from the session). The export is generated in the background\nand a secure download link is emailed within 24h." + }, + "LimitIncreaseRequest": { + "properties": { + "kind": { + "type": "string", + "enum": [ + "document_scale", + "session_scale", + "other" + ], + "title": "Kind", + "description": "What you hit: 'document_scale' = a single document crossed the per-document page limit (413 DOCUMENT_TOO_COMPLEX); 'session_scale' = the documents open in one chat crossed the per-chat limit (413 SESSION_TOO_FULL); 'other' = anything else (explain in note).", + "default": "document_scale" + }, + "attempted_pages": { + "anyOf": [ + { + "type": "integer", + "maximum": 10000000.0, + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Attempted Pages", + "description": "Approximate page count you were trying to work with (from the 413 message)." + }, + "attempted_sections": { + "anyOf": [ + { + "type": "integer", + "maximum": 10000000.0, + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Attempted Sections", + "description": "Exact section count from the 413 detail (section_count / document_section_count), if you have it." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "The file that hit the limit, if any." + }, + "note": { + "anyOf": [ + { + "type": "string", + "maxLength": 2000 + }, + { + "type": "null" + } + ], + "title": "Note", + "description": "Anything else that helps us size your limits (workload, cadence, deadline)." + } + }, + "type": "object", + "title": "LimitIncreaseRequest", + "description": "Body for request_limit_increase \u2014 the one-call limit-expansion request." + }, + "OpenDocumentsRequest": { + "properties": { + "document_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Document Ids", + "description": "Durable document ids (from GET /v1/documents) to open into the session." + } + }, + "type": "object", + "required": [ + "document_ids" + ], + "title": "OpenDocumentsRequest" + }, + "PendingChange": { + "properties": { + "change_id": { + "type": "string", + "title": "Change Id", + "description": "Unique identifier for this proposed change." + }, + "operation": { + "type": "string", + "title": "Operation", + "description": "Type of change: 'edit', 'create', or 'delete'." + }, + "chunk_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Chunk Id", + "description": "Target document section ID being modified or deleted." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Identifier of the document (tab) this change applies to \u2014 lets multi-document integrators attribute each section diff to the right document." + }, + "old_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Old Html", + "description": "Previous HTML content of the section (for updates)." + }, + "new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Html", + "description": "Proposed new HTML content (for updates and creates)." + }, + "ai_explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Explanation", + "description": "AI-generated explanation of why this change was proposed." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "change_id", + "operation" + ], + "title": "PendingChange", + "description": "A proposed document change awaiting user approval." + }, + "ProcessDocumentResponse": { + "properties": { + "html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Html" + }, + "session_id": { + "type": "string", + "title": "Session Id" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "chunks_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Chunks Count" + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id" + }, + "job_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Job Id" + }, + "status": { + "type": "string", + "title": "Status" + }, + "parse_mode": { + "type": "string", + "title": "Parse Mode" + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup" + }, + "warnings": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Warnings" + } + }, + "type": "object", + "required": [ + "session_id", + "filename", + "status", + "parse_mode" + ], + "title": "ProcessDocumentResponse" + }, + "ProcessUploadRequest": { + "properties": { + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Session ID to load this document/attachment into. Must match the chat/async session-id format \u2014 letters, digits, '_', '-', '.' only (no ':'); max 256 chars." + }, + "filename": { + "type": "string", + "maxLength": 255, + "title": "Filename", + "description": "Same filename passed to request_upload_url." + }, + "parse_mode": { + "type": "string", + "enum": [ + "document", + "attachment" + ], + "title": "Parse Mode", + "description": "'document' = parse and load as the active editable doc; 'attachment' = process asynchronously as AI-searchable reference (returns job_id to poll).", + "default": "document" + }, + "return_html": { + "type": "boolean", + "title": "Return Html", + "description": "When false (default), the response is compact \u2014 metadata only (chunks_count, version_id, page_setup), no document body \u2014 to keep your context small; the document is loaded into the session and you edit it via chat. Set true only if you actually need the parsed HTML returned inline.", + "default": false + } + }, + "type": "object", + "required": [ + "session_id", + "filename" + ], + "title": "ProcessUploadRequest" + }, + "ReEditChunkRequest": { + "properties": { + "user_current_html": { + "type": "string", + "title": "User Current Html" + }, + "ai_original_old_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Original Old Html" + }, + "ai_proposed_new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Proposed New Html" + }, + "ai_explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Explanation" + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier" + }, + "mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Mode", + "default": "redo" + } + }, + "type": "object", + "required": [ + "user_current_html" + ], + "title": "ReEditChunkRequest", + "description": "Re-apply the AI's intended change on the user's current section HTML, OR (mode='merge')\ncombine the user's version and the AI's version into one." + }, + "ReEditChunkResponse": { + "properties": { + "chunk_id": { + "type": "string", + "title": "Chunk Id" + }, + "new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Html" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + } + }, + "type": "object", + "required": [ + "chunk_id" + ], + "title": "ReEditChunkResponse" + }, + "RedeemRequest": { + "properties": { + "code": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "title": "Code", + "description": "The promo code (case-insensitive)." + } + }, + "type": "object", + "required": [ + "code" + ], + "title": "RedeemRequest", + "description": "Redeem a promo code and receive its credit grant." + }, + "RedeemResponse": { + "properties": { + "status": { + "type": "string", + "title": "Status", + "description": "Always 'redeemed' on success.", + "default": "redeemed" + }, + "promotion": { + "$ref": "#/components/schemas/RedemptionOut" + }, + "message": { + "type": "string", + "title": "Message" + } + }, + "type": "object", + "required": [ + "promotion", + "message" + ], + "title": "RedeemResponse", + "description": "Response body for POST /v1/promo/redeem on success." + }, + "RedemptionOut": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + } + }, + "type": "object", + "required": [ + "id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at" + ], + "title": "RedemptionOut", + "description": "Summary of a successful redemption, returned from POST /v1/promo/redeem." + }, + "RedoRequest": { + "properties": { + "turn_index": { + "type": "integer", + "minimum": 0.0, + "title": "Turn Index", + "description": "The turn the revert rewound to (rows at/after this were archived); redo un-archives exactly those." + }, + "redo_checkpoint_id": { + "type": "string", + "title": "Redo Checkpoint Id", + "description": "The identifier of the pre-revert state, returned by the revert response." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window issuing the request, used to coordinate concurrent reverts from multiple browser tabs." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "turn_index", + "redo_checkpoint_id" + ], + "title": "RedoRequest", + "description": "Undo a revert \u2014 restore the captured pre-revert state and the rolled-back\nconversation + document. Meant for use immediately after a revert (before sending a new message,\nwhich would diverge the timeline); the web app gates it to that window." + }, + "RevertRequest": { + "properties": { + "turn_index": { + "type": "integer", + "minimum": 0.0, + "title": "Turn Index", + "description": "Turn index of the user message to revert. The text of that message is returned in compose_text so the caller can re-edit and resend it. Every chat row at this turn or later is soft-archived (hidden from active reads but retained for audit)." + }, + "dry_run": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Dry Run", + "description": "When true, compute the per-document revert diff WITHOUT committing or archiving anything \u2014 used to render a preview before the user confirms.", + "default": false + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window issuing the revert, used to coordinate concurrent reverts from multiple browser tabs." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "turn_index" + ], + "title": "RevertRequest", + "description": "Rewind a chat session to the state immediately before a specific user message." + }, + "RevertResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier." + }, + "reverted_to_turn": { + "type": "integer", + "title": "Reverted To Turn", + "description": "Turn index that the active conversation now ends at (the AI reply preceding the reverted user message). -1 when the session is reset to its initial empty state (revert from the very first user message)." + }, + "compose_text": { + "type": "string", + "title": "Compose Text", + "description": "The text of the user message that was reverted. Clients should pre-fill the compose box with this so the user can edit and resend." + }, + "document_state": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentState" + }, + { + "type": "null" + } + ], + "description": "Restored document state. Null when the session is reset to empty." + }, + "editor_action": { + "type": "string", + "title": "Editor Action", + "description": "Client instruction: 'update' to load the restored document, or 'clear' to reset the editor to empty.", + "default": "update" + }, + "archived_turn_count": { + "type": "integer", + "title": "Archived Turn Count", + "description": "Number of chat rows soft-archived by this revert (turns hidden from the UI but retained for audit)." + }, + "revert_changes": { + "anyOf": [ + { + "additionalProperties": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Revert Changes", + "description": "Per-document (slug -> change-set) revert diff. Applying these merges the revert onto any concurrent live edits \u2014 a conflicting section is surfaced for you to resolve rather than replacing the whole document. On dry_run this is the preview; on a real revert it is what was committed." + }, + "dry_run": { + "type": "boolean", + "title": "Dry Run", + "description": "Echoes the request's dry_run: when true nothing was committed/archived \u2014 this is a preview only.", + "default": false + }, + "redo_checkpoint_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Redo Checkpoint Id", + "description": "The identifier of the pre-revert state. Revert is non-destructive \u2014 passing this back to POST /sessions/{id}/redo (with the same turn_index) restores it and un-archives the rolled-back turns, undoing the revert. Null on a dry-run or a first-message reset." + } + }, + "type": "object", + "required": [ + "session_id", + "reverted_to_turn", + "compose_text", + "archived_turn_count" + ], + "title": "RevertResponse", + "description": "Result of a session revert: restored document state and compose-box prefill." + }, + "SaveDocumentRequest": { + "properties": { + "html": { + "type": "string", + "title": "Html" + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup" + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id" + }, + "base_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Html" + }, + "touched_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Touched Chunk Ids" + }, + "deleted_part_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Deleted Part Chunk Ids" + } + }, + "type": "object", + "required": [ + "html" + ], + "title": "SaveDocumentRequest", + "description": "Body for the human-edit autosave." + }, + "SessionInfo": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Unique session identifier." + }, + "user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User who owns this session (null for organization sessions)." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the session was created." + }, + "last_activity": { + "type": "string", + "title": "Last Activity", + "description": "ISO 8601 timestamp of the most recent activity." + }, + "message_count": { + "type": "integer", + "title": "Message Count", + "description": "Total number of messages in this session." + }, + "preview": { + "type": "string", + "title": "Preview", + "description": "Preview of the first user message (up to 100 characters)." + } + }, + "type": "object", + "required": [ + "session_id", + "created_at", + "last_activity", + "message_count", + "preview" + ], + "title": "SessionInfo", + "description": "Summary of a document editing session." + }, + "SessionListResponse": { + "properties": { + "sessions": { + "items": { + "$ref": "#/components/schemas/SessionInfo" + }, + "type": "array", + "title": "Sessions", + "description": "Array of session summaries, ordered by most recent activity." + }, + "total": { + "type": "integer", + "title": "Total", + "description": "Total number of sessions returned." + } + }, + "type": "object", + "required": [ + "sessions", + "total" + ], + "title": "SessionListResponse", + "description": "List of document editing sessions." + }, + "UniversalChatRequest": { + "properties": { + "message": { + "type": "string", + "maxLength": 100000, + "title": "Message", + "description": "Your message to the AI assistant." + }, + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Unique session identifier. Reuse to continue a conversation." + }, + "document_html": { + "anyOf": [ + { + "type": "string", + "maxLength": 50000000 + }, + { + "type": "null" + } + ], + "title": "Document Html", + "description": "Current document HTML. OMIT this when the session already holds the document \u2014 the server persists it across turns, so you don't re-send it every turn; pass it only to load new content or replace the document wholesale (a verbatim load \u2014 the AI never re-types content passed here). Include data-chunk-id attributes on elements to enable targeted AI edits." + }, + "user_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User identifier. Automatically set from authentication \u2014 typically omit this." + }, + "image_attachments": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ImageAttachmentData" + }, + "type": "array", + "maxItems": 20 + }, + { + "type": "null" + } + ], + "title": "Image Attachments", + "description": "Inline images for vision-based analysis (base64-encoded)." + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier", + "description": "AI model to use: 'core' (default, fast), 'turbo' (fastest), 'pro' (advanced reasoning), 'max' (most capable)." + }, + "thinking_depth": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Thinking Depth", + "description": "Reasoning depth: 'fast', 'balanced' (default), or 'deep'. Controls how much analysis the AI performs." + }, + "approval_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Approval Mode", + "description": "Change review mode: 'approve_all' (default, auto-applies changes) or 'ask_every_time' (pauses for your review)." + }, + "response_mode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "full", + "compact" + ] + }, + { + "type": "null" + } + ], + "title": "Response Mode", + "description": "Response shape control. 'full' (default) returns the complete updated document HTML \u2014 required by web app editors and recommended for small documents (<20 pages). 'compact' (recommended for AI agents editing large documents) suppresses the full HTML and returns only per-section diffs (chunk_diffs) for changed sections, saving thousands of tokens per turn. To read sections in compact mode, just send a natural-language request like 'show me the force majeure section' \u2014 the AI returns the content in the chat reply text.", + "default": "full" + }, + "cursor_context": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Cursor Context", + "description": "Editor cursor context at message time, used to default the insert location for new images, diagrams, or sections when the user's prompt doesn't name a position. Shape: {chunk_id: str, pos_in_chunk?: int, text_before?: str, text_after?: str}. Omit when the user typed in chat without focusing the editor first." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Target a specific open document by id (ids come from /v1/sessions/{session_id}/documents). Omit to target the focused document (default \u2014 unchanged single-document behaviour). When set, that document becomes the focus for this turn." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window (set by the web app) so concurrent edits from two windows of the same chat are merged rather than overwritten. Agents/API may omit." + }, + "cross_session_memory": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory", + "description": "When true, the AI carries a private rolling memory of context across THIS account's chats (off by default). Per-request override of your saved Settings default." + }, + "cross_session_search": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Search", + "description": "When true, the AI may search your PAST chats and documents on demand to reuse prior work and open a found document (off by default). Per-request override of your saved Settings default; never affects the always-on search of the open document." + }, + "cross_session_memory_key": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory Key", + "description": "Optional per-request id of YOUR end-customer so the cross-session memory note is kept in a separate file per end-customer. Omit for one account-level note (the default / B2C)." + }, + "cross_session_scope": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 2000 + }, + { + "type": "null" + } + ], + "title": "Cross Session Scope", + "description": "Optional list of session ids to restrict cross-session SEARCH to (e.g. one end-customer's sessions). Omit to search all of this account's sessions. Only narrows within the API-key owner; ignored unless cross_session_search is on." + }, + "touched_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Touched Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) the user actually edited since the last sync, sent alongside document_html. When present, a chunk NOT in this list whose text is unchanged keeps its stored formatting byte-for-byte even if the client serialized it differently (protects styling from editor round-trip loss). Chunks in the list \u2014 and any chunk whose text changed \u2014 always take the submitted content. Omit for the default behavior (any differing chunk is treated as an edit)." + }, + "deleted_part_chunk_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 50000 + }, + { + "type": "null" + } + ], + "title": "Deleted Part Chunk Ids", + "description": "Optional list of chunk ids (data-chunk-id values) of OUT-OF-FLOW part sections (page headers/footers, footnote/endnote bodies, comments) to delete explicitly, sent alongside document_html. Out-of-flow parts absent from document_html are always KEPT \u2014 an editor view not containing them is their normal state, never a deletion \u2014 so removing one requires naming its id here. Ids that are not stored out-of-flow parts are ignored (in-flow content keeps the default behavior). Omit when deleting nothing." + } + }, + "type": "object", + "required": [ + "message", + "session_id" + ], + "title": "UniversalChatRequest", + "description": "Send a message to the AI assistant with optional document context.", + "examples": [ + { + "approval_mode": "approve_all", + "document_html": "

Section 3

Terms and conditions...

", + "message": "Add a confidentiality clause to section 3", + "model_tier": "core", + "session_id": "session_abc123" + } + ] + }, + "UpdateDocumentRequest": { + "properties": { + "title": { + "anyOf": [ + { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + { + "type": "null" + } + ], + "title": "Title", + "description": "New document title." + }, + "parts": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Parts", + "description": "Document-parts patch \u2014 the document's OUT-OF-FLOW content, keyed by any of: sections, headers, footers, footnotes, endnotes, comments. Each key you send REPLACES that whole part family; a null value clears it; keys you omit are untouched. Every HTML fragment is sanitized server-side. Headers/footers are keyed by section index with default/first/even variants; dynamic page fields use cached text." + }, + "base_parts": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Base Parts", + "description": "Optional concurrency guard: the parts subtree your edit was based on (as last read). If another writer changed one of the same part families since, your write still lands (newest-write-wins) and the overwritten families are reported in `parts_conflicts`." + } + }, + "type": "object", + "title": "UpdateDocumentRequest", + "description": "PATCH /v1/documents/{id} body \u2014 rename a document and/or update its out-of-flow\nparts. Both fields optional; at least one must be present." + }, + "UpdateProfileRequest": { + "properties": { + "display_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Display Name", + "description": "New display name." + }, + "timezone": { + "anyOf": [ + { + "type": "string", + "maxLength": 100 + }, + { + "type": "null" + } + ], + "title": "Timezone", + "description": "IANA timezone (e.g., 'America/New_York')." + }, + "language": { + "anyOf": [ + { + "type": "string", + "maxLength": 10 + }, + { + "type": "null" + } + ], + "title": "Language", + "description": "Preferred language code (e.g., 'en', 'es')." + }, + "preferences": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Preferences", + "description": "User preferences to update (e.g., {approval_mode: 'ask_every_time', model_tier: 'pro'})." + } + }, + "type": "object", + "title": "UpdateProfileRequest", + "description": "Update user profile fields." + }, + "UploadImageBase64Request": { + "properties": { + "image_base64": { + "type": "string", + "title": "Image Base64", + "description": "The image bytes, base64-encoded (raw base64 or a data: URL)." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Optional original filename (used only to infer content type)." + }, + "content_type": { + "anyOf": [ + { + "type": "string", + "maxLength": 100 + }, + { + "type": "null" + } + ], + "title": "Content Type", + "description": "Optional MIME type, e.g. image/png. Defaults to image/png." + } + }, + "type": "object", + "required": [ + "image_base64" + ], + "title": "UploadImageBase64Request" + }, + "UploadUrlRequest": { + "properties": { + "filename": { + "type": "string", + "maxLength": 255, + "title": "Filename", + "description": "Original filename including extension (e.g. 'contract.docx'). Used downstream for file-type detection." + }, + "content_type": { + "type": "string", + "maxLength": 200, + "title": "Content Type", + "description": "MIME type the agent will PUT with (e.g. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' for .docx)." + }, + "size_bytes": { + "type": "integer", + "maximum": 104857600.0, + "exclusiveMinimum": 0.0, + "title": "Size Bytes", + "description": "File size in bytes. Must be > 0 and <= 104857600 (100 MB)." + }, + "purpose": { + "type": "string", + "enum": [ + "document", + "attachment", + "export-html" + ], + "title": "Purpose", + "description": "What this file will be used for. 'document' = the active editable doc; 'attachment' = read-only AI-searchable reference; 'export-html' = HTML payload destined for /v1/documents/export (large-export upload flow, for documents above ~25 MB).", + "default": "document" + } + }, + "type": "object", + "required": [ + "filename", + "content_type", + "size_bytes" + ], + "title": "UploadUrlRequest" + }, + "UploadUrlResponse": { + "properties": { + "upload_id": { + "type": "string", + "title": "Upload Id" + }, + "upload_url": { + "type": "string", + "title": "Upload Url" + }, + "expires_at": { + "type": "string", + "title": "Expires At" + }, + "expires_in_seconds": { + "type": "integer", + "title": "Expires In Seconds" + }, + "max_size_bytes": { + "type": "integer", + "title": "Max Size Bytes" + }, + "curl_example": { + "type": "string", + "title": "Curl Example" + } + }, + "type": "object", + "required": [ + "upload_id", + "upload_url", + "expires_at", + "expires_in_seconds", + "max_size_bytes", + "curl_example" + ], + "title": "UploadUrlResponse" + }, + "UsageInfo": { + "properties": { + "monthly_used": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Used", + "description": "Total operations used this billing cycle." + }, + "monthly_limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Limit", + "description": "Maximum operations allowed per cycle. -1 means unlimited." + }, + "monthly_remaining": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Remaining", + "description": "Operations remaining this cycle. -1 means unlimited." + }, + "was_billable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Was Billable", + "description": "Whether this request counted as a billable operation." + }, + "ops_charged": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Ops Charged", + "description": "How many operations this request billed. A large multi-section edit can bill more than one (one per 25 sections edited), so monthly_used can increase by more than 1 between responses." + }, + "quota_exhausted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Quota Exhausted", + "description": "True when you have reached your plan's operation limit with no remaining balance \u2014 the current request still completes, but further billable requests pause until you upgrade or your billing cycle resets." + }, + "subscription_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Subscription Tier", + "description": "Current subscription tier: 'free', 'plus', 'pro', or 'enterprise'." + }, + "bucket_used": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bucket Used", + "description": "Which bucket this operation drew from: 'tier' or 'promo'. Null for non-billable ops." + }, + "redemption_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Redemption Id", + "description": "Redemption grant this op drew from, when bucket_used is 'promo'." + }, + "promotions": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ActivePromotionInfo" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Promotions", + "description": "User's currently-active promotion grants, ordered by oldest-expiring first." + } + }, + "additionalProperties": true, + "type": "object", + "title": "UsageInfo", + "description": "Operation usage data for the current billing cycle." + }, + "UsageStatsResponse": { + "properties": { + "user_id": { + "type": "string", + "title": "User Id", + "description": "User identifier." + }, + "subscription_tier": { + "type": "string", + "title": "Subscription Tier", + "description": "Current plan: 'free', 'plus', 'pro', or 'enterprise'." + }, + "monthly_limit": { + "type": "integer", + "title": "Monthly Limit", + "description": "Maximum operations allowed per month. -1 means unlimited." + }, + "monthly_used": { + "type": "integer", + "title": "Monthly Used", + "description": "Operations used this billing cycle." + }, + "monthly_remaining": { + "type": "integer", + "title": "Monthly Remaining", + "description": "Operations remaining this cycle." + }, + "monthly_reset_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Monthly Reset At", + "description": "ISO 8601 timestamp when the monthly counter resets." + }, + "total_sessions": { + "type": "integer", + "title": "Total Sessions", + "description": "Total number of chat sessions created." + }, + "total_documents": { + "type": "integer", + "title": "Total Documents", + "description": "Total number of documents processed." + }, + "total_operations": { + "type": "integer", + "title": "Total Operations", + "description": "Lifetime total operations across all billing cycles." + }, + "current_month_stats": { + "additionalProperties": true, + "type": "object", + "title": "Current Month Stats", + "description": "Breakdown of this month's operations by type, including counts, tokens used, and success rates." + } + }, + "type": "object", + "required": [ + "user_id", + "subscription_tier", + "monthly_limit", + "monthly_used", + "monthly_remaining", + "total_sessions", + "total_documents", + "total_operations", + "current_month_stats" + ], + "title": "UsageStatsResponse", + "description": "Detailed usage statistics for the current billing cycle." + }, + "UserProfileResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Unique user identifier (UUID)." + }, + "email": { + "type": "string", + "title": "Email", + "description": "User's email address." + }, + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Display Name", + "description": "User's display name." + }, + "photo_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Photo Url", + "description": "URL to user's profile photo." + }, + "email_verified": { + "type": "boolean", + "title": "Email Verified", + "description": "Whether the user's email has been verified." + }, + "auth_provider": { + "type": "string", + "title": "Auth Provider", + "description": "Authentication method: 'email' or 'google'." + }, + "subscription_tier": { + "type": "string", + "title": "Subscription Tier", + "description": "Current plan: 'free', 'plus', 'pro', or 'enterprise'." + }, + "subscription_status": { + "type": "string", + "title": "Subscription Status", + "description": "Subscription status: 'active', 'canceled', or 'past_due'." + }, + "monthly_operation_limit": { + "type": "integer", + "title": "Monthly Operation Limit", + "description": "Maximum operations allowed per month. -1 means unlimited." + }, + "monthly_operations_used": { + "type": "integer", + "title": "Monthly Operations Used", + "description": "Operations used this billing cycle." + }, + "monthly_remaining": { + "type": "integer", + "title": "Monthly Remaining", + "description": "Operations remaining this cycle." + }, + "preferences": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Preferences", + "description": "User preferences (e.g., approval_mode, model_tier)." + }, + "created_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At", + "description": "ISO 8601 timestamp when the account was created." + }, + "last_login_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Login At", + "description": "ISO 8601 timestamp of the last login." + } + }, + "type": "object", + "required": [ + "id", + "email", + "email_verified", + "auth_provider", + "subscription_tier", + "subscription_status", + "monthly_operation_limit", + "monthly_operations_used", + "monthly_remaining" + ], + "title": "UserProfileResponse", + "description": "User profile with subscription and usage information." + }, + "UserPromotionsResponse": { + "properties": { + "active": { + "items": { + "$ref": "#/components/schemas/ActivePromotionOut" + }, + "type": "array", + "title": "Active" + }, + "history": { + "items": { + "$ref": "#/components/schemas/HistoricalPromotionOut" + }, + "type": "array", + "title": "History" + } + }, + "type": "object", + "required": [ + "active", + "history" + ], + "title": "UserPromotionsResponse", + "description": "Listing of the authenticated user's promotion grants, split by active vs. history." + }, + "UserSessionResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Unique session identifier." + }, + "last_message_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Message At", + "description": "ISO 8601 timestamp of the most recent message." + }, + "message_count": { + "type": "integer", + "title": "Message Count", + "description": "Total number of messages in this session." + } + }, + "type": "object", + "required": [ + "session_id", + "message_count" + ], + "title": "UserSessionResponse", + "description": "Summary of a user's chat session." + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + }, + "securitySchemes": { + "HTTPBearer": { + "type": "http", + "scheme": "bearer" + } + } + } +} \ No newline at end of file diff --git a/extensions/doctask2-oviya-senthilkumar/specs/superdocs-old.json b/extensions/doctask2-oviya-senthilkumar/specs/superdocs-old.json new file mode 100644 index 00000000..b99cc0d0 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/specs/superdocs-old.json @@ -0,0 +1,7364 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Universal Document AI API", + "description": "AI-powered document editing with multi-tenant organization support", + "version": "2.0.0" + }, + "paths": { + "/v1/chat": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Edit, draft, or restructure a document using natural language. Preserves tables, styling, and formatting.", + "description": "Synchronous AI chat that can rewrite specific paragraphs, add or remove table rows, restructure sections, generate new content from templates, or transform an entire document. Pass document_html only to load or replace the document; once a session holds a document the server persists it across turns, so omit it on follow-up turns. Returns AI response text plus structural document changes (HTML edits, additions, deletions) with chunk IDs. One billable operation per document-modifying turn; very large multi-section edits bill one operation per 25 sections changed. For long-running edits or human-in-the-loop approval, use chat_async. Optional: model_tier (core/turbo/pro/max), thinking_depth (fast/balanced/deep), image_attachments for multimodal vision. RECOMMENDED for AI agents working with large documents (>20 pages): set response_mode='compact' to skip the full HTML in the response (the AI returns only per-section diffs in chunk_diffs) and save thousands of tokens per turn. To read sections in compact mode, just send a natural-language request like 'show me the force majeure section' \u2014 the AI returns the content in the reply text. Always use natural language to describe what you want; the AI handles all internal section lookups.", + "operationId": "chat", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UniversalChatRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "List your active document editing sessions to resume or audit prior work.", + "description": "Returns sessions sorted by most recent activity, with message counts and last-updated timestamps. Each session represents one document with full edit history and AI conversation persisted server-side. Use to find a previous editing context to resume (then call get_session_history) or to audit your workspace.", + "operationId": "list_sessions", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter by user ID (deprecated - auth determines scope)", + "title": "User Id" + }, + "description": "Filter by user ID (deprecated - auth determines scope)" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of sessions to return", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of sessions to return" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents": { + "get": { + "tags": [ + "v1", + "mcp" + ], + "summary": "List your saved documents (the Files view).", + "description": "List the authenticated user/org's saved documents, most-recently-updated first\n(metadata only \u2014 no document content). Each carries a session_count (\"N chats\"). Documents\nbecome durable + reusable automatically as you create or edit them; on the FIRST call we also\none-time import the documents of pre-Files-view chats so prior work appears here too.", + "operationId": "list_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1, + "description": "Maximum number of documents to return.", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of documents to return." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Offset for pagination.", + "default": 0, + "title": "Offset" + }, + "description": "Offset for pagination." + }, + { + "name": "include_preview", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include a small preview_html (first chunks) per document for thumbnails. The web Files view sets this; agents leave it off to stay token-light.", + "default": false, + "title": "Include Preview" + }, + "description": "Include a small preview_html (first chunks) per document for thumbnails. The web Files view sets this; agents leave it off to stay token-light." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/cross-session-memory": { + "delete": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Clear your cross-session memory note.", + "description": "Delete the caller's cross-session memory note. Owner-scoped: a caller can only clear its OWN\nnote. With no key this clears the account-level note; with a memory_key it clears that one\nend-customer's note. Idempotent (removed=0 if it didn't exist).", + "operationId": "clear_cross_session_memory", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "memory_key", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Clear ONE end-customer's stored memory (the cross_session_memory_key you send on chat). Omit to clear the account-level note (the B2C default).", + "title": "Memory Key" + }, + "description": "Clear ONE end-customer's stored memory (the cross_session_memory_key you send on chat). Omit to clear the account-level note (the B2C default)." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/{document_id}": { + "get": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Get a saved document's detail and the chats that used it.", + "description": "One saved document's metadata plus the chat sessions that have it open\n(prior-chats-per-file). Owner-scoped \u2014 404 if the document isn't yours.\nToken-light by default; pass include_html=true to also get the full HTML body.", + "operationId": "get_document_detail", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, also returns the document's full reassembled HTML body (`html`), plus its `page_setup` and `version_id`. Default false returns metadata + prior-chats only, keeping the response token-light. This is the by-id way to read a saved document's body without opening it into a session.", + "default": false, + "title": "Include Html" + }, + "description": "When true, also returns the document's full reassembled HTML body (`html`), plus its `page_setup` and `version_id`. Default false returns metadata + prior-chats only, keeping the response token-light. This is the by-id way to read a saved document's body without opening it into a session." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "patch": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Rename a saved document.", + "description": "Rename one of your saved documents.", + "operationId": "rename_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenameDocumentRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Delete (archive) a saved document.", + "description": "Soft-archive a saved document \u2014 it leaves the Files view but is recoverable server-side.\nPresence-aware: if the document is currently open in ANOTHER session and `force` is not set,\nreturn an in-use warning \u2014 HTTP 200 (NOT archived) carrying `code:\"document_in_use\"` +\n`held_by_other:true` + `open_in_sessions:N` so an agent or UI can branch on it and confirm\n\"open in N other session(s) \u2014 delete anyway?\" then re-call with `force=true`. On `force=true`\nit archives, unlinks every session, and notifies the other sessions (which then prompt the user\nto keep editing or honor the deletion).", + "operationId": "archive_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "force", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Confirm deletion of a doc that is open in another session. Without it, deleting an in-use doc returns a held_by_other warning instead of tombstoning.", + "default": false, + "title": "Force" + }, + "description": "Confirm deletion of a doc that is open in another session. Without it, deleting an in-use doc returns a held_by_other warning instead of tombstoning." + }, + { + "name": "from_session", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The caller's own session id (so it is excluded from the in-use check). Omit for a pure Files-view delete.", + "title": "From Session" + }, + "description": "The caller's own session id (so it is excluded from the in-use check). Omit for a pure Files-view delete." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/{document_id}/unarchive": { + "post": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Restore (un-archive) a previously archived document by its id.", + "description": "Restore (un-archive) a saved document by its id \u2014 the mirror of archive_document. The document\nre-enters your Files view and can be opened/edited again (open it into a chat with open_documents).\n`document_id` is the same id used by list_documents / archive_document. Idempotent: restoring an\nalready-active (or unknown) document is a no-op. Non-billable.", + "operationId": "unarchive_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/open": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Open saved documents into a chat session (shared, never copied).", + "description": "Load one or more SAVED documents into a session as editable tabs. The SAME durable\ndocument is attached (never copied), so edits flow back to the one shared row with\ncross-session soft-collaboration. The first listed document is focused. Returns the\nrefreshed document roster so the client can render tabs immediately.", + "operationId": "open_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenDocumentsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/init": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Start a new chat session and open documents into it in one call.", + "description": "Create a session AND open N saved documents into it in ONE call (the documents are SHARED,\nnever copied \u2014 the same cross-session behavior as /documents/open). The web UI passes its own\nsession_id so its session model stays consistent; an MCP/API integrator can omit it and the\nserver mints one \u2014 the one-call way to start a session with documents already open. With no\ndocument_ids it just returns a fresh, empty session. The open semantics (first document focused,\ndurable binding, returned roster) are identical to /documents/open.", + "operationId": "init_session", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InitSessionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/save": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Persist a user-edited document (non-AI autosave).", + "description": "Persist a HUMAN-edited document \u2014 the editor's debounced autosave + save-on-blur \u2014 WITHOUT\nan AI turn. Re-indexes the html into the target document (preserving its durable identity so it\nUPDATES the same Files entry), then saves it so pure typing is preserved AND other sessions with\nthe same document open stay in sync. The AI-edit flow is unaffected (autosave saves first; a\nlater AI result is merged with your saved edits). Non-billable, REST-only (a UI affordance, not an MCP tool).", + "operationId": "save_human_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SaveDocumentRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/unarchive": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Restore a document another session archived, keeping your current edits (web app).", + "description": "Restore (un-archive) a document that was archived, re-linking it to this session and bringing\nits content back. `document_id` is the session-local id (same as /save); the durable id is\nrecovered from the cached document. Idempotent: restoring an already-active (or never-archived)\ndocument is a no-op. Non-billable.\n\nTwo shapes: WITHOUT a body (e.g. an AI agent restoring a document by id) the archived content is\nrestored as-is; WITH a body carrying the current editor HTML (the web app's \"keep editing\n(restores it)\" choice after another session deleted the document) the document is restored AND the\nsupplied edits are saved on top, so it converges for everyone.", + "operationId": "restore_document_keep_editing", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SaveDocumentRequest" + }, + { + "type": "null" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/blank": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Open a new blank document as a tab in the session.", + "description": "Open a fresh BLANK document as a new focused tab in the session (the tab-strip \"+\").\nNon-billable \u2014 no AI, no upload pipeline; it is saved on first edit.\nREST-only (a manual UI affordance, not an MCP tool \u2014 agents create documents through chat).", + "operationId": "new_blank_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "List the documents open in a multi-document session.", + "description": "Returns the editable documents open in this session (focused first), each with its id, chunk\ncount, and focused flag \u2014 so a client can render document tabs. The response is token-light by\ndefault; pass include_html=true to also get each document's reassembled HTML.", + "operationId": "list_session_documents", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "report_changed", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Resume awareness: when true, each doc carries `changed_since_seen` = its committed version is newer than the version THIS session last saw (changed by another session while away). The web UI sets this ONLY on resume / a chat switch (never on the ~2s poll) so it can show a one-time 'changed since you were last here' notice \u2014 ongoing changes are surfaced through the normal change feed. Computed before the roster updates the version this session has seen.", + "default": false, + "title": "Report Changed" + }, + "description": "Resume awareness: when true, each doc carries `changed_since_seen` = its committed version is newer than the version THIS session last saw (changed by another session while away). The web UI sets this ONLY on resume / a chat switch (never on the ~2s poll) so it can show a one-time 'changed since you were last here' notice \u2014 ongoing changes are surfaced through the normal change feed. Computed before the roster updates the version this session has seen." + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/doc-events": { + "get": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Session Document Events", + "description": "Poll for cross-session document updates: returns `document_updated` events for documents\nTHIS session holds that OTHER sessions committed since `after_id`. The client polls this\n(~every 1-2s while a doc is open) and re-fetches the changed document's content so all open\nsessions converge without a refresh. Excludes this session's own writes by default (the web UI\nalready has its own changes); a REST/MCP integrator can pass `include_own=true` to receive its\nown events too. REST-only \u2014 a lightweight poll, deliberately NOT an MCP tool and NOT a held\nSSE connection (keeps the per-instance connection budget free).", + "operationId": "session_document_events_v1_sessions__session_id__doc_events_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "after_id", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Return events with id greater than this (the poll cursor).", + "default": 0, + "title": "After Id" + }, + "description": "Return events with id greater than this (the poll cursor)." + }, + { + "name": "include_own", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Integrator opt-out: when true, ALSO return this session's own document_updated events (default false excludes them \u2014 the web UI never needs its own writes echoed). A REST/MCP integration that writes on one connection and polls on another, or wants a complete change feed for the docs it holds, sets this true.", + "default": false, + "title": "Include Own" + }, + "description": "Integrator opt-out: when true, ALSO return this session's own document_updated events (default false excludes them \u2014 the web UI never needs its own writes echoed). A REST/MCP integration that writes on one connection and polls on another, or wants a complete change feed for the docs it holds, sets this true." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}/focus": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Switch which document is focused in a multi-document session.", + "description": "Make document_id the focused document (the editor's active document and the default edit\ntarget). Persists the outgoing focused document into the session's document map and returns the\nnow-focused document's HTML.", + "operationId": "focus_session_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/documents/{document_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Close (remove) a document from a multi-document session.", + "description": "Remove document_id from the session's open documents (the tab close button). If it's the\nfocused document, focus another open document \u2014 `next_focus` if it's still open (so the client\ncan pick the adjacent tab), else the next remaining one; closing the last document leaves the\nsession empty. Returns the updated document roster + the now-focused document's HTML.\n\nThe close takes effect immediately and is persisted, so a later reconnect or session restore\nwon't bring the closed document back. Persistence is best-effort \u2014 a failure is logged, not\nfatal (the close still holds for the active session).", + "operationId": "close_session_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "document_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Document Id" + } + }, + { + "name": "next_focus", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Next Focus" + } + }, + { + "name": "include_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content.", + "default": false, + "title": "Include Html" + }, + "description": "Include each document's full reassembled HTML body. Default false (token-light \u2014 recommended for AI agents enumerating or switching tabs): returns only ids, title, section count, focused flag, and page setup, omitting the body which can be hundreds of KB per document. The web app passes true to render document content." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/chunks/{chunk_id}/re-edit": { + "post": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Re-apply the AI's intended edit on top of the user's current version of one section.", + "description": "Resolve a concurrent-edit conflict on ONE section. When a user edited a section while the AI\nwas also changing it, this re-applies the AI's intended change on top of the user's current text\n(mode=\"redo\"), or blends the user's and AI's versions into one (mode=\"merge\"). Returns only the\nrewritten section HTML. Performs one AI edit and counts as one billable operation.", + "operationId": "re_edit_chunk_on_user_version", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "chunk_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Chunk Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReEditChunkRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReEditChunkResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/history": { + "get": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Restore the full conversation and document state for a previous session.", + "description": "Returns complete message history (user and AI), final document HTML with chunk IDs preserved, attachment list, and editor actions (font, color, alignment changes). Use to continue editing a document you started in a prior session. The AI rehydrates with full context of all prior decisions, chunk IDs, and attachments. Pass include_document_html=false to skip the full document body when you only need the conversation.", + "operationId": "get_session_history", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "focus_document_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Durable id of the document to focus on restore (e.g. the file the user clicked in the Files view). When set, that document is shown in the editor and marked focused in the roster; omit to use the session's own last-focused document.", + "title": "Focus Document Id" + }, + "description": "Durable id of the document to focus on restore (e.g. the file the user clicked in the Files view). When set, that document is shown in the editor and marked focused in the roster; omit to use the session's own last-focused document." + }, + { + "name": "include_document_html", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When false, omit the restored document's full HTML body (document_state.html_content) to keep your context small \u2014 agents that just need the conversation + metadata should set false. Default true (the web app needs the body to re-render the editor).", + "default": true, + "title": "Include Document Html" + }, + "description": "When false, omit the restored document's full HTML body (document_state.html_content) to keep your context small \u2014 agents that just need the conversation + metadata should set false. Default true (the web app needs the body to re-render the editor)." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnhancedChatHistoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/revert": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Rewind a chat session to before a specific user message \u2014 restores both the document and the conversation in one call.", + "description": "Rewinds a chat session to the state immediately before a specific user message. The document, conversation history, and supporting context all snap back to that point. The text of the reverted message is returned (compose_text) so the caller can edit and resend it. Messages from the reverted turn forward are soft-archived: hidden from active reads but retained for audit. The original conversation is preserved server-side; the restored conversation becomes the active timeline. Rejects with 409 if the session has a chat job in progress or awaiting approval \u2014 wait for the job to settle before reverting. Returns 422 if the message predates the revertability feature.", + "operationId": "revert_session_to_message", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/redo": { + "post": { + "tags": [ + "v1", + "mcp", + "sessions" + ], + "summary": "Undo a revert: restore the pre-revert state and the rolled-back turns.", + "description": "Revert is non-destructive: this restores the session FORWARD to the pre-revert state\n(returned by the revert as redo_checkpoint_id), restores the document to that state while still\nmerging any concurrent edits from other sessions (keeping both where they conflict), and\nun-archives exactly the chat turns the revert hid. Intended for use immediately after a revert;\nonce a new message is sent the timeline diverges and the web app stops offering it.", + "operationId": "redo_revert", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedoRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevertResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/upload": { + "post": { + "tags": [ + "v1", + "attachments" + ], + "summary": "Upload Attachment", + "description": "Upload a document attachment to a session for AI reference.\n\nThe file is processed asynchronously \u2014 text is extracted, converted, and indexed\nso the AI can search and reference it during chat. Use GET /v1/attachments/status/{session_id}\nto check processing progress.\n\nSupported file types: .pdf, .docx, .txt, .rtf, .md, .html, .htm (max 50 MB).\nReturns a job_id for tracking the processing status.", + "operationId": "upload_attachment", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_attachment" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/{attachment_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Remove an attachment from a session or cancel its in-progress processing.", + "description": "Removes the attachment from the session; the AI will no longer reference it in subsequent chat turns. If processing is still in progress, also cancels the underlying job. Use to free up context, remove sensitive files mid-session, or replace a stale reference document. The attachment_id can be either the final attachment ID or the job ID (during processing).", + "operationId": "delete_attachment", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "attachment_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Attachment Id" + } + }, + { + "name": "session_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/status/{session_id}": { + "get": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Check processing status of all attachments in a session.", + "description": "Returns each attachment's processing state (pending/processing/completed/failed) plus extracted text length and chunk count once ready. Poll this after upload_attachment_base64 to know when an attachment becomes queryable by the AI. Surfaces processing errors with actionable messages.", + "operationId": "get_attachment_status", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/upload": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Upload Document To Editor", + "description": "Upload a document file and load it into the editor.\n\nConverts the file to HTML with formatting preserved (tables, colors, images).\nImages are extracted to cloud storage and referenced by URL.\nReturns the HTML for the editor to display.\n\nAI indexing happens automatically on the first chat message.\nFor API clients who want immediate indexing, pass ?index=true.", + "operationId": "upload_document_to_editor", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "index", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Set to 'true' for immediate AI indexing (API clients). Default: conversion only.", + "title": "Index" + }, + "description": "Set to 'true' for immediate AI indexing (API clients). Default: conversion only." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_document_to_editor" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/images/upload": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Upload an image and get back a stable URL the editor can drop into .", + "description": "Upload a single inline image and return a stable URL you can reference\nin the document via .\n\nAccepts PNG/JPEG/WebP/GIF/SVG, up to 10 MB per upload. The returned URL is\npublic-read with an unguessable path. Useful for saving a drawing or\nscreenshot so a document can embed it.", + "operationId": "upload_inline_image", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_inline_image" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/export": { + "post": { + "tags": [ + "v1", + "mcp" + ], + "summary": "Export the current document as a styled .docx (default), .pdf, .html, .md, or .txt file with full fidelity.", + "description": "Round-trips the document through the original docx renderer, preserving tables, borders, shading, alternating row colors, headers, footers, fonts, inline styling, and embedded images. Two modes: pass html directly to export ad-hoc content, OR pass session_id to export the session's current state. Format options: 'docx' (default, native Open XML, best for programmatic processing or mail merge), 'pdf', 'html', 'markdown', or 'txt' ('doc' is a legacy Word-compatible HTML alias). Fidelity is the differentiator vs naive HTML to docx converters.", + "operationId": "export_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExportRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/export/email-request": { + "post": { + "tags": [ + "v1", + "documents" + ], + "summary": "Request Large Export Email", + "description": "Enqueue a large-export job \u2014 runs in the background and emails a\nsecure, time-limited download link when the export finishes. 24h SLA\npromised in the email.", + "operationId": "request_large_export_email", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LargeExportEmailRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/documents/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "documents" + ], + "summary": "Upload .docx/PDF/HTML/MD/RTF as the active editable document with chunk-ID structural editing.", + "description": "Parses the file into structured HTML where every paragraph, heading, table, row, and cell has a unique chunk ID, enabling the AI to make targeted structural edits via chat (\"remove row 3 of the pricing table\" works). Tables, borders, shading, alternating row colors, fonts, and inline styling are preserved on edit and export. Also works for AI-generated content: if you've drafted an outline or partial document in your context, upload it here as the working doc, then use chat to fill in the rest. When you pass a session_id the response is compact by default (metadata only \u2014 pass return_html=true for the full parsed HTML); the document is loaded into the session and you edit it via chat. For files >100KB, prefer request_upload_url (pre-signed URL flow) to avoid token bloat from base64 through the agent context. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm. Max 50 MB.", + "operationId": "upload_document_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/attachments/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "attachments" + ], + "summary": "Upload a reference file (PDF/DOCX/image) for the AI to query while editing.", + "description": "Files are processed asynchronously and become AI-searchable once ready. The AI can then reference the attachment's content during chat (e.g., \"rewrite section 3 to match the style guide PDF I attached\"). Images are queryable via multimodal vision. Poll get_attachment_status to know when ready. Distinct from upload_document_base64: attachments are read-only context for the AI to reference, not the editable working document. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm. Max 50 MB. Requires session_id.", + "operationId": "upload_attachment_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/upload-base64": { + "post": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "Save a document template (NDA, contract, SOP, letterhead) for reuse across sessions.", + "description": "Templates persist across sessions and can be referenced by the AI when drafting new documents (e.g., \"draft an NDA using my standard template\"). Stored at user or organization scope. Ideal for boilerplate, branded letterheads, recurring document structures, or compliance-required templates. Supports the same formats as document upload: .docx, .pdf, .txt, .rtf, .md, .html, .htm. Max 50 MB.", + "operationId": "upload_template_base64", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Base64UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "List your async chat jobs (in-progress, awaiting approval, completed, failed).", + "description": "Returns jobs sorted by most recent, optionally filtered by status. Use to monitor long-running AI edits started via chat_async, see which jobs are paused waiting for human approval, or audit completed work. Each job tracks the chat that started it, the changes made, and any HITL decisions logged.", + "operationId": "list_jobs", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter by job status", + "title": "Status" + }, + "description": "Filter by job status" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of jobs to return", + "default": 50, + "title": "Limit" + }, + "description": "Maximum number of jobs to return" + }, + { + "name": "compact", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs.", + "default": false, + "title": "Compact" + }, + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs/{job_id}": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "Get the status, partial results, and any pending changes for an async chat job.", + "description": "Returns the job's current state (pending, in_progress, awaiting_approval, completed, failed, or cancelled), intermediate AI responses streamed during execution, the final document HTML once complete, and any pending changes awaiting user approval (with chunk IDs and proposed HTML diffs in metadata.pending_changes). Poll this after chat_async to track progress and retrieve results. When status is awaiting_approval, call POST /v1/chat/{session_id}/approve to approve or deny each pending change. Headless/MCP clients get the same typed progress events the web SSE stream delivers via metadata.intermediate_responses (e.g. documents_changed, continue_prompt, model_fallback, proposed_change_batch); cross-session document updates from OTHER sessions are reported separately by polling GET /v1/sessions/{session_id}/doc-events.", + "operationId": "get_job", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Job Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/jobs/{job_id}/cancel": { + "post": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "Cancel a pending or in-progress async chat job.", + "description": "Stops the AI mid-edit. Already-applied changes are preserved in the document; pending changes are discarded. Use to abort long-running operations that are no longer needed (e.g., user changed their mind, or you want to retry with different parameters or model_tier). Only jobs with status pending or processing can be cancelled. Returns the updated job details with status cancelled.", + "operationId": "cancel_job", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Job Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/sessions/{session_id}/jobs": { + "get": { + "tags": [ + "v1", + "mcp", + "jobs" + ], + "summary": "List all async chat jobs for a specific session, most recent first.", + "description": "Returns the full job history of one document. Useful for auditing what the AI did to a document over time, or finding a specific job that's waiting for HITL approval. Same shape as list_jobs but scoped to one session.", + "operationId": "get_session_jobs", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Maximum number of jobs to return", + "default": 20, + "title": "Limit" + }, + "description": "Maximum number of jobs to return" + }, + { + "name": "compact", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs.", + "default": false, + "title": "Compact" + }, + "description": "When true, omit each job's heavy result body + streamed intermediate events \u2014 returns just status/progress/ids/timestamps. Recommended for agents listing many jobs." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/async": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Start a long-running or HITL-approved AI edit; returns a job_id to poll for results.", + "description": "Use instead of chat when (a) the edit is large or multi-step, (b) you need human approval on each proposed change before it applies (set approval_mode='ask_every_time'), or (c) you can't afford to block on a synchronous response. Returns a job_id immediately. Poll get_job to track progress and retrieve the final document. Approve or deny pending changes via approve_change. Job state is durable and survives server restarts. Optional: model_tier (core/turbo/pro/max), thinking_depth (fast/balanced/deep), image_attachments for multimodal vision. RECOMMENDED for AI agents working with large documents (>20 pages): set response_mode='compact' so the job result skips the full HTML and surfaces only per-section diffs in chunk_diffs, saving thousands of tokens per poll. To read sections in compact mode, just send a natural-language chat request ('show me the pricing section') \u2014 the AI returns the content in the reply text. Always use natural language; the AI handles all internal section lookups. HITL workflow (approval_mode='ask_every_time'): 1) poll get_job until status=awaiting_approval, 2) read metadata.pending_changes for proposed edits, 3) call approve_change per change, 4) continue polling until status=completed.", + "operationId": "chat_async", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncChatRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncChatResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/stream": { + "get": { + "tags": [ + "v1", + "chat" + ], + "summary": "Stream Chat Progress", + "description": "Stream real-time progress for a chat job using Server-Sent Events (SSE).\n\nOpens an SSE connection that streams events as the AI processes your request.\nUse this with the job_id returned from POST /v1/chat/async.\n\nEvent types:\n- 'intermediate': Progress updates during processing (content, sequence, timestamp).\n- 'proposed_change_batch': The batch of document changes proposed for review, delivered as one\n event carrying changes[] (emitted when approval_mode is 'ask_every_time'). Changes are always\n delivered as a batch via this event.\n- 'document_sync': Chunk-id sync emitted before the agent runs, so changes can reference stable ids.\n- 'continue_prompt': A pause on a large edit, asking whether to continue or stop.\n- 'documents_changed': Signals that one or more documents were auto-applied (with per-document\n change counts and changed chunk ids).\n- 'model_fallback': Notice that the request automatically failed over to another model tier.\n- 'final': Processing complete. Contains the full result with AI response and document changes.\n- 'usage': Billing data emitted after 'final' (monthly_used, monthly_limit, monthly_remaining).\n- 'error': An error occurred (job failed, cancelled, or not found).\n\nAuthentication: Pass a token or api_key as a query parameter (required for EventSource which cannot set headers).", + "operationId": "stream_chat_progress_v1_chat__session_id__stream_get", + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "job_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "description": "Job ID to stream progress for", + "title": "Job Id" + }, + "description": "Job ID to stream progress for" + }, + { + "name": "last_sequence", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "SSE Last-Event-ID resume: only replay intermediate responses with sequence > this. Reconnects pass the highest sequence already processed so old events are NOT re-emitted; default 0 = full history.", + "default": 0, + "title": "Last Sequence" + }, + "description": "SSE Last-Event-ID resume: only replay intermediate responses with sequence > this. Reconnects pass the highest sequence already processed so old events are NOT re-emitted; default 0 = full history." + }, + { + "name": "token", + "in": "query", + "required": false, + "schema": { + "type": "string", + "description": "Authentication token (query parameter for EventSource compatibility)", + "title": "Token" + }, + "description": "Authentication token (query parameter for EventSource compatibility)" + }, + { + "name": "api_key", + "in": "query", + "required": false, + "schema": { + "type": "string", + "description": "API key (query parameter for EventSource compatibility)", + "title": "Api Key" + }, + "description": "API key (query parameter for EventSource compatibility)" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/approve": { + "post": { + "tags": [ + "v1", + "mcp", + "approval" + ], + "summary": "Approve or deny AI-proposed document changes one-by-one or in batch (HITL workflow).", + "description": "Used with chat_async when approval_mode='ask_every_time'. For each proposed change (with chunk_id and HTML diff), respond approved=true|false plus optional feedback for the AI to revise on. Approved changes apply atomically; denied changes are discarded; the AI may revise based on feedback in the next turn. Required for regulated workflows (legal, medical, compliance) where every AI edit must be reviewed before it touches the document. For single changes: set approved=true/false and optionally provide feedback. For batch decisions: provide a 'changes' array with per-change decisions. After approval, the job resumes processing and eventually reaches status=completed.", + "operationId": "approve_change", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApprovalRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/chat/{session_id}/continue": { + "post": { + "tags": [ + "v1", + "mcp", + "chat" + ], + "summary": "Resume or stop a chat turn paused by a large-edit continue prompt.", + "description": "Used with chat_async when a large edit paused to ask whether to keep going.\nPoll get_job until status=awaiting_approval AND metadata.awaiting_kind='continue_prompt';\nthen POST here with continue=true to resume (the AI picks up where it left off with a\nfresh time/step budget) or continue=false to stop (everything applied so far is kept).\nThe job then resumes/finishes and reaches status=completed. This is NOT the change-\napproval endpoint (that is approve_change) \u2014 it can only act on a continue-prompt pause.", + "operationId": "continue_chat", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContinueRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/poll/{session_id}": { + "get": { + "tags": [ + "v1", + "sessions" + ], + "summary": "Poll Session Updates", + "description": "Long-poll for real-time job updates on a session (B2B organizations only).\n\nReturns immediately if there are recent job updates, or holds the connection\nopen up to the specified timeout. Use the 'since' parameter to avoid receiving\nduplicate updates.", + "operationId": "poll_session_updates_v1_poll__session_id__get", + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + }, + { + "name": "since", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "ISO 8601 timestamp \u2014 only return updates after this time", + "title": "Since" + }, + "description": "ISO 8601 timestamp \u2014 only return updates after this time" + }, + { + "name": "timeout", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "description": "Long polling timeout in seconds (max 1800 = 30 minutes)", + "default": 1800, + "title": "Timeout" + }, + "description": "Long polling timeout in seconds (max 1800 = 30 minutes)" + }, + { + "name": "authorization", + "in": "header", + "required": true, + "schema": { + "type": "string", + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/upload": { + "post": { + "tags": [ + "v1", + "templates" + ], + "summary": "Upload User Template", + "description": "Upload a document file as a personal/organization template.\n\nThe file is processed synchronously: text extraction, HTML conversion, and content indexing.\nSupported formats: .docx, .pdf, .txt, .rtf, .md, .html, .htm\nTemplates are scoped to the uploading user or organization.", + "operationId": "upload_user_template", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_user_template" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates": { + "get": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "List all saved document templates available to the user or organization.", + "description": "Returns active (non-deleted) templates with name, format, size, and creation date metadata. Use to show the AI what reusable document structures are available for drafting new documents. Templates are scoped to the authenticated entity: users via the web app or sk_ key see only their own templates; organizations via lce_ key see theirs.", + "operationId": "list_user_templates", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/templates/{template_id}": { + "delete": { + "tags": [ + "v1", + "mcp", + "templates" + ], + "summary": "Delete a saved document template by ID.", + "description": "Soft-deletes the template; only the owner (user or organization) can delete. Once deleted, the template no longer appears in list_user_templates and cannot be referenced by the AI for new documents. Existing documents already drafted from the template are unaffected.", + "operationId": "delete_user_template", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "template_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Template Id" + } + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me": { + "get": { + "tags": [ + "users" + ], + "summary": "Get Current User Profile", + "description": "Get current user's profile information\n\nReturns user profile with subscription tier and usage limits", + "operationId": "get_current_user_profile_v1_users_me_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserProfileResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "patch": { + "tags": [ + "users" + ], + "summary": "Update User Profile", + "description": "Update current user's profile\n\nAllows updating display name, timezone, language, and preferences", + "operationId": "update_user_profile_v1_users_me_patch", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProfileRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/usage": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Usage Stats", + "description": "Get detailed usage statistics for current user\n\nReturns operation counts, token usage, and success rates by operation type", + "operationId": "get_user_usage_stats_v1_users_me_usage_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UsageStatsResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/sessions": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Sessions", + "description": "List all chat sessions for current user\n\nReturns list of sessions with last activity and message counts", + "operationId": "get_user_sessions_v1_users_me_sessions_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/UserSessionResponse" + }, + "type": "array", + "title": "Response Get User Sessions V1 Users Me Sessions Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/sessions/{session_id}": { + "delete": { + "tags": [ + "users" + ], + "summary": "Delete User Session", + "description": "Delete a specific chat session\n\nRemoves all messages for the given session ID (user must own the session)", + "operationId": "delete_user_session_v1_users_me_sessions__session_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Session Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/limits": { + "get": { + "tags": [ + "users" + ], + "summary": "Check User Limits", + "description": "Check current usage limits and remaining operations\n\nReturns real-time usage information with reset date", + "operationId": "check_user_limits_v1_users_me_limits_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/api-keys": { + "get": { + "tags": [ + "users" + ], + "summary": "List Api Keys", + "description": "List all API keys for the current user (masked).\n\nReturns key prefix and last 4 characters for identification.", + "operationId": "list_api_keys_v1_users_me_api_keys_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ApiKeyListItem" + }, + "type": "array", + "title": "Response List Api Keys V1 Users Me Api Keys Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "users" + ], + "summary": "Create Api Key", + "description": "Create a new API key for the current user.\n\nThe raw key is returned ONCE in the response. It cannot be retrieved again.", + "operationId": "create_api_key_v1_users_me_api_keys_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateApiKeyResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/v1/users/me/api-keys/{key_id}": { + "delete": { + "tags": [ + "users" + ], + "summary": "Revoke Api Key", + "description": "Revoke (soft delete) an API key.\n\nThe key will be marked as inactive and can no longer be used for authentication.", + "operationId": "revoke_api_key_v1_users_me_api_keys__key_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "key_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Key Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/promo/redeem": { + "post": { + "tags": [ + "promo" + ], + "summary": "Redeem Promo", + "description": "Redeem a promo code and add the granted operations to the authenticated user's account.\n\nB2C only: web app login tokens and sk_ user API keys are accepted; lce_ org keys are rejected.\nRequires a verified email. Each user may redeem a given code at most once.\n\nRate-limited to 3 attempts per IP per hour. All attempts (success and failure) are\nlogged for audit.", + "operationId": "redeem_promo_v1_promo_redeem_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedeemRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RedeemResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/users/me/promotions": { + "get": { + "tags": [ + "promo" + ], + "summary": "List My Promotions", + "description": "List the authenticated user's promotion redemptions, split into `active` (drawable now)\nand `history` (exhausted / expired / revoked).\n\nUsed by the Settings \u2192 Billing tab to render the Credits & Promotions section.", + "operationId": "list_my_promotions_v1_users_me_promotions_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPromotionsResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/uploads": { + "post": { + "tags": [ + "uploads", + "mcp", + "uploads" + ], + "summary": "Get a pre-signed URL to upload large files (.docx/PDF/HTML/MD/RTF) without bloating agent context.", + "description": "Returns a short-lived (5-minute) PUT URL plus a curl example. **If you have shell access (Bash tool), execute the returned `curl_example` yourself to push the file directly to cloud storage \u2014 do NOT paste the curl command back to the user expecting them to run it manually. Only fall back to returning the curl text when shell execution is genuinely unavailable.** Bytes never pass through the agent's context window either way. After upload completes, call process_uploaded_document with the upload_id to trigger parsing. For files <100KB where token cost is trivial, upload_document_base64 still works inline. Max file size: 100 MB. Supported formats: .pdf, .docx, .txt, .rtf, .md, .html, .htm.", + "operationId": "request_upload_url", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadUrlRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/uploads/{upload_id}/process": { + "post": { + "tags": [ + "uploads", + "mcp", + "uploads" + ], + "summary": "Parse an uploaded file into structured HTML with chunk IDs for targeted AI editing.", + "description": "Fetches the file uploaded via request_upload_url and runs the same parsing pipeline as upload_document_base64: every paragraph, heading, table, row, and cell gets a unique chunk ID, enabling targeted structural edits via chat (\"remove row 3 of the pricing table\" works), with tables, borders, shading, alternating row colors, fonts, and inline styling preserved on edit and export. By default the response is compact (metadata only, no document body) to keep your context small \u2014 the document is loaded into the session and you edit it via chat; pass return_html=true if you need the parsed HTML inline. Uploading and parsing is NOT itself a billable operation \u2014 you are charged only when the AI edits the document. Specify parse_mode='document' to load as the active editable document, or parse_mode='attachment' to load as a read-only AI-searchable reference.", + "operationId": "process_uploaded_document", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "upload_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The upload_id returned by request_upload_url.", + "title": "Upload Id" + }, + "description": "The upload_id returned by request_upload_url." + }, + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessUploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessDocumentResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/downloads": { + "post": { + "tags": [ + "uploads", + "mcp", + "downloads" + ], + "summary": "Get a pre-signed URL to download an exported document without proxying through the agent.", + "description": "Returns a short-lived (15-minute) GET URL plus a curl example. **If you have shell access (Bash tool), execute the returned `curl_example` yourself to download the file to the user's working directory \u2014 do NOT paste the curl command back to the user expecting them to run it manually. Only fall back to returning the URL+command as text when shell execution is genuinely unavailable, OR when the user has explicitly asked for the URL only.** Bytes never pass through the agent's context window either way. Generates the document in the requested format and returns a time-limited signed download URL. Specify format as 'pdf', 'docx', 'html', 'markdown', or 'txt' (legacy 'doc' also accepted).", + "operationId": "request_download_url", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Authorization" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadUrlRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/": { + "get": { + "summary": "Root", + "operationId": "root__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health": { + "get": { + "tags": [ + "mcp" + ], + "summary": "Verify the SuperDocs MCP server is reachable and serving traffic.", + "description": "Returns 200 with {\"status\":\"healthy\"} when the API is up. Call this once after MCP install to confirm the connection works before invoking other tools. No authentication required.", + "operationId": "health", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health/deep": { + "get": { + "summary": "Health Check Deep", + "description": "Deep health check that verifies the job queue, AI workflow state store, and database are all responsive within a bounded time budget. Used by container orchestrators as a liveness probe to distinguish \"process alive and TCP listening\" (what /health reports) from \"application is actually serving requests\" (this endpoint).", + "operationId": "health_check_deep_health_deep_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/oauth-protected-resource": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource_mcp__well_known_oauth_protected_resource_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-protected-resource/mcp": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource__well_known_oauth_protected_resource_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-protected-resource": { + "get": { + "summary": "Oauth Protected Resource", + "operationId": "oauth_protected_resource__well_known_oauth_protected_resource_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/oauth-authorization-server": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server_mcp__well_known_oauth_authorization_server_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-authorization-server/mcp": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server__well_known_oauth_authorization_server_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/oauth-authorization-server": { + "get": { + "summary": "Oauth Authorization Server", + "operationId": "oauth_authorization_server__well_known_oauth_authorization_server_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/mcp/.well-known/openid-configuration": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration_mcp__well_known_openid_configuration_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/openid-configuration/mcp": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration__well_known_openid_configuration_mcp_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/.well-known/openid-configuration": { + "get": { + "summary": "Openid Configuration", + "operationId": "openid_configuration__well_known_openid_configuration_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ActivePromotionInfo": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id", + "description": "Unique identifier for this promotion grant." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Human-friendly promotion name (e.g., 'YC SUS India 2026 cohort')." + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining", + "description": "Operations still available in this promotion bucket." + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted", + "description": "Total operations originally granted by this redemption." + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At", + "description": "ISO 8601 expiry timestamp. Null if the credits never expire." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_remaining", + "ops_granted" + ], + "title": "ActivePromotionInfo", + "description": "A single active (drawable) promotion belonging to the authenticated user." + }, + "ActivePromotionOut": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + } + }, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at" + ], + "title": "ActivePromotionOut", + "description": "A single active (drawable) promotion grant belonging to the current user." + }, + "ApiKeyListItem": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Key identifier (UUID)." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Label assigned to this key." + }, + "key_prefix": { + "type": "string", + "title": "Key Prefix", + "description": "First 7 characters of the key (e.g., 'sk_a1b2')." + }, + "last_four": { + "type": "string", + "title": "Last Four", + "description": "Last 4 characters of the key for identification." + }, + "is_active": { + "type": "boolean", + "title": "Is Active", + "description": "Whether the key is active. Revoked keys show as false." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the key was created." + }, + "last_used_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Used At", + "description": "ISO 8601 timestamp of last use, or null if never used." + } + }, + "type": "object", + "required": [ + "id", + "name", + "key_prefix", + "last_four", + "is_active", + "created_at" + ], + "title": "ApiKeyListItem", + "description": "API key summary (masked for security)." + }, + "ApprovalRequest": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job ID that is awaiting approval." + }, + "change_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Change Id", + "description": "Specific change to approve/deny (for individual decisions). Get change IDs from the job's pending_changes." + }, + "approved": { + "type": "boolean", + "title": "Approved", + "description": "Whether to approve (true) or deny (false) the change." + }, + "feedback": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Feedback", + "description": "Optional feedback explaining why a change was denied. Helps the AI adjust future suggestions." + }, + "changes": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Changes", + "description": "Batch decisions: array of {change_id, approved, feedback} objects. Use for 'Accept All' or 'Deny All'." + } + }, + "type": "object", + "required": [ + "job_id", + "approved" + ], + "title": "ApprovalRequest", + "description": "Approve or deny proposed AI document changes." + }, + "AsyncChatRequest": { + "properties": { + "message": { + "type": "string", + "maxLength": 100000, + "title": "Message", + "description": "Your message to the AI assistant." + }, + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Unique session identifier. Reuse to continue a conversation." + }, + "document_html": { + "anyOf": [ + { + "type": "string", + "maxLength": 50000000 + }, + { + "type": "null" + } + ], + "title": "Document Html", + "description": "Current document HTML. OMIT this when the session already holds the document \u2014 the server persists it across turns, so you don't re-send it every turn; pass it only to load new content or replace the document wholesale. Include data-chunk-id attributes on elements to enable targeted AI edits." + }, + "user_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User identifier. Automatically set from authentication \u2014 typically omit this." + }, + "image_attachments": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ImageAttachmentData" + }, + "type": "array", + "maxItems": 20 + }, + { + "type": "null" + } + ], + "title": "Image Attachments", + "description": "Inline images for vision-based analysis (base64-encoded)." + }, + "async_mode": { + "type": "boolean", + "title": "Async Mode", + "description": "Must be true for async processing. Included for backward compatibility.", + "default": true + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier", + "description": "AI model to use: 'core' (default, fast), 'turbo' (fastest), 'pro' (advanced reasoning), 'max' (most capable)." + }, + "thinking_depth": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Thinking Depth", + "description": "Reasoning depth: 'fast', 'balanced' (default), or 'deep'. Controls how much analysis the AI performs." + }, + "approval_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Approval Mode", + "description": "Change review mode: 'approve_all' (default, auto-applies changes) or 'ask_every_time' (pauses for your review)." + }, + "response_mode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "full", + "compact" + ] + }, + { + "type": "null" + } + ], + "title": "Response Mode", + "description": "Response shape control. 'full' (default) puts the complete updated document HTML in the job result \u2014 required by web app editors and recommended for small documents. 'compact' (recommended for AI agents editing large documents) suppresses the full HTML and stores only per-section diffs (chunk_diffs), saving thousands of tokens when polling get_job. To read sections in compact mode, send a natural-language request to chat ('show me the pricing section') \u2014 the AI returns the content in the reply text.", + "default": "full" + }, + "cursor_context": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Cursor Context", + "description": "Editor cursor context at message time, used to default the insert location for new images, diagrams, or sections when the user's prompt doesn't name a position. Shape: {chunk_id: str, pos_in_chunk?: int, text_before?: str, text_after?: str}. Omit when the user typed in chat without focusing the editor first." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Target a specific open document by id (ids come from /v1/sessions/{session_id}/documents). Omit to target the focused document (default \u2014 unchanged single-document behaviour). When set, that document becomes the focus for this turn." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window (set by the web app) so concurrent edits from two windows of the same chat are merged rather than overwritten. Agents/API may omit." + }, + "cross_session_memory": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory", + "description": "When true, the AI carries a private rolling memory of context across THIS account's chats (off by default). Per-request override of your saved Settings default." + }, + "cross_session_search": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Search", + "description": "When true, the AI may search your PAST chats and documents on demand to reuse prior work and open a found document (off by default). Per-request override of your saved Settings default; never affects the always-on search of the open document." + }, + "cross_session_memory_key": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory Key", + "description": "Optional per-request id of YOUR end-customer so the cross-session memory note is kept in a separate file per end-customer. Omit for one account-level note (the default / B2C)." + }, + "cross_session_scope": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 2000 + }, + { + "type": "null" + } + ], + "title": "Cross Session Scope", + "description": "Optional list of session ids to restrict cross-session SEARCH to (e.g. one end-customer's sessions). Omit to search all of this account's sessions. Only narrows within the API-key owner; ignored unless cross_session_search is on." + } + }, + "type": "object", + "required": [ + "message", + "session_id" + ], + "title": "AsyncChatRequest", + "description": "Start an async chat request that processes in the background." + }, + "AsyncChatResponse": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job identifier. Poll GET /v1/jobs/{job_id} for status and results." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier for this conversation." + }, + "status": { + "type": "string", + "title": "Status", + "description": "Initial job status (always 'pending')." + }, + "message": { + "type": "string", + "title": "Message", + "description": "Human-readable status message.", + "default": "Chat request queued for processing" + } + }, + "type": "object", + "required": [ + "job_id", + "session_id", + "status" + ], + "title": "AsyncChatResponse", + "description": "Response from starting an async chat request." + }, + "Base64UploadRequest": { + "properties": { + "filename": { + "type": "string", + "maxLength": 512, + "title": "Filename", + "description": "Original filename with extension (e.g. 'contract.pdf'). Used for file type detection." + }, + "file_base64": { + "type": "string", + "maxLength": 50000000, + "title": "File Base64", + "description": "Base64-encoded file content." + }, + "session_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session ID. Auto-generated if not provided." + }, + "return_html": { + "type": "boolean", + "title": "Return Html", + "description": "With a session_id: false (default) returns compact metadata only (chunks_count, version_id, page_setup) \u2014 the document is loaded into the session and you edit it via chat, keeping your context small; true returns the full parsed HTML inline. Ignored on the convert-only path (no session_id), which always returns the converted html.", + "default": false + } + }, + "type": "object", + "required": [ + "filename", + "file_base64" + ], + "title": "Base64UploadRequest", + "description": "JSON request body for base64-encoded file uploads." + }, + "Body_upload_attachment": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id" + }, + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "session_id", + "file" + ], + "title": "Body_upload_attachment" + }, + "Body_upload_document_to_editor": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id" + }, + "open_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Open Mode", + "default": "replace" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_document_to_editor" + }, + "Body_upload_inline_image": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_inline_image" + }, + "Body_upload_user_template": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_user_template" + }, + "ChatResponse": { + "properties": { + "response": { + "type": "string", + "title": "Response", + "description": "AI assistant's response text." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier for this conversation." + }, + "document_changes": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentChanges" + }, + { + "type": "null" + } + ], + "description": "Document modifications made by the AI. Present only when the document was changed." + }, + "usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/UsageInfo" + }, + { + "type": "null" + } + ], + "description": "Operation usage data. Present for authenticated users with usage tracking." + } + }, + "type": "object", + "required": [ + "response", + "session_id" + ], + "title": "ChatResponse", + "description": "AI response with optional document changes and usage data.", + "examples": [ + { + "document_changes": { + "changes_summary": "Document updated by AI", + "updated_html": "

Section 3

Updated content...

", + "version_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7" + }, + "response": "I've added a confidentiality clause to section 3 covering non-disclosure obligations.", + "session_id": "session_abc123", + "usage": { + "monthly_limit": 500, + "monthly_remaining": 458, + "monthly_used": 42, + "subscription_tier": "free", + "was_billable": true + } + } + ] + }, + "ContinueRequest": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Job ID awaiting a continue decision." + }, + "continue": { + "type": "boolean", + "title": "Continue", + "description": "True to resume with a fresh budget; false to stop here (work so far is kept)." + } + }, + "type": "object", + "required": [ + "job_id", + "continue" + ], + "title": "ContinueRequest", + "description": "Resume or stop a chat turn that paused to ask whether to continue." + }, + "CreateApiKeyRequest": { + "properties": { + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1, + "title": "Name", + "description": "A label for this key (e.g., 'My App', 'CI Pipeline')." + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "CreateApiKeyRequest", + "description": "Create a new API key." + }, + "CreateApiKeyResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Key identifier (UUID). Use this to revoke the key later." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Label you assigned to this key." + }, + "key": { + "type": "string", + "title": "Key", + "description": "The full API key (sk_... format). Copy this now \u2014 it cannot be retrieved again." + }, + "key_prefix": { + "type": "string", + "title": "Key Prefix", + "description": "First 7 characters of the key (e.g., 'sk_a1b2') for identification." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the key was created." + } + }, + "type": "object", + "required": [ + "id", + "name", + "key", + "key_prefix", + "created_at" + ], + "title": "CreateApiKeyResponse", + "description": "Newly created API key. The raw key is shown only once \u2014 save it immediately." + }, + "DocumentChanges": { + "properties": { + "updated_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Updated Html", + "description": "Full updated document HTML with data-chunk-id attributes. Apply this to your editor to sync the document. Present in 'full' response mode (default); null in 'compact' mode." + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id", + "description": "Document version identifier. Changes on every modification." + }, + "changes_summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Changes Summary", + "description": "Human-readable summary of what was changed." + }, + "requires_approval": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Requires Approval", + "description": "If true, changes need review via the approve endpoint before being applied." + }, + "pending_changes": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pending Changes", + "description": "Proposed changes awaiting approval. Present when requires_approval is true." + }, + "changes": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Changes", + "description": "History of individual changes applied during this request." + }, + "chunk_diffs": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Chunk Diffs", + "description": "Per-section before/after content for changes that were applied this turn. Present in 'compact' response mode (when updated_html is null) so the agent can verify what was modified without paying token cost for the full document. Same shape as pending_changes." + } + }, + "additionalProperties": true, + "type": "object", + "title": "DocumentChanges", + "description": "Document modifications produced by the AI assistant." + }, + "DocumentState": { + "properties": { + "html_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Html Content", + "description": "Full document HTML content with data-chunk-id attributes." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Session-local id (slug) of the focused document. Identifies the document on restore so a restored tab stays in sync with edits made to it in other sessions." + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id", + "description": "Document version identifier (UUID string)." + }, + "chunk_count": { + "type": "integer", + "title": "Chunk Count", + "description": "Number of content sections in the document.", + "default": 0 + }, + "last_modified": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Modified", + "description": "ISO 8601 timestamp of the last modification." + }, + "attachments": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Attachments", + "description": "Session attachments. Each item has: id (string), filename (string), file_extension (string), processing_status ('ready', 'processing', or 'failed')." + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup", + "description": "Source page geometry of the focused document (width_in, height_in, margin_in{top,right,bottom,left}, orientation) when known, else null. The editor renders the page at this geometry on restore; null \u21d2 US-Letter + 1in default." + } + }, + "type": "object", + "title": "DocumentState", + "description": "Current document state for session restoration." + }, + "DownloadUrlRequest": { + "properties": { + "session_id": { + "type": "string", + "maxLength": 256, + "title": "Session Id", + "description": "Session ID whose current document should be exported and packaged for download." + }, + "format": { + "type": "string", + "enum": [ + "docx", + "pdf", + "html", + "markdown", + "txt", + "doc" + ], + "title": "Format", + "description": "Export format (docx, pdf, html, markdown, txt).", + "default": "docx" + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Optional Content-Disposition filename for the download (without extension). Auto-detected from the document's first heading if not provided." + }, + "options": { + "$ref": "#/components/schemas/ExportOptions", + "description": "Customization (paper, margins, filename, watermark, etc.). Mirrors ExportRequest.options." + } + }, + "type": "object", + "required": [ + "session_id" + ], + "title": "DownloadUrlRequest" + }, + "DownloadUrlResponse": { + "properties": { + "download_url": { + "type": "string", + "title": "Download Url" + }, + "expires_at": { + "type": "string", + "title": "Expires At" + }, + "expires_in_seconds": { + "type": "integer", + "title": "Expires In Seconds" + }, + "curl_example": { + "type": "string", + "title": "Curl Example" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "format": { + "type": "string", + "title": "Format" + } + }, + "type": "object", + "required": [ + "download_url", + "expires_at", + "expires_in_seconds", + "curl_example", + "filename", + "format" + ], + "title": "DownloadUrlResponse" + }, + "EnhancedChatHistoryResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier." + }, + "messages": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Messages", + "description": "Ordered list of messages. Each item has: id (string), sender ('user' or 'ai'), content (string), timestamp (ISO 8601), turn_index (int), checkpoint_id (string or null \u2014 present on turns recorded after the revertability feature shipped; null for legacy turns where revert is unavailable)." + }, + "document_state": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentState" + }, + { + "type": "null" + } + ], + "description": "Current document state. Present if the session has an active document." + }, + "editor_action": { + "type": "string", + "title": "Editor Action", + "description": "Client instruction: 'update' (load document_state HTML into editor), 'clear' (reset editor), or 'keep' (no change).", + "default": "keep" + } + }, + "type": "object", + "required": [ + "session_id", + "messages" + ], + "title": "EnhancedChatHistoryResponse", + "description": "Conversation history with document state and restoration instructions." + }, + "ExportOptions": { + "properties": { + "paper_size": { + "type": "string", + "enum": [ + "A4", + "Letter", + "A3", + "Legal" + ], + "title": "Paper Size", + "description": "Page size for DOCX/PDF/legacy .doc. HTML/MD/TXT exports ignore.", + "default": "Letter" + }, + "orientation": { + "type": "string", + "enum": [ + "portrait", + "landscape" + ], + "title": "Orientation", + "description": "Page orientation for DOCX/PDF/legacy .doc.", + "default": "portrait" + }, + "margins": { + "type": "string", + "enum": [ + "narrow", + "normal", + "wide", + "custom" + ], + "title": "Margins", + "description": "Page margins preset. 'narrow' = 0.5in, 'normal' = 1.0in, 'wide' = 1.5in. 'custom' uses custom_margins_inches.", + "default": "normal" + }, + "custom_margins_inches": { + "anyOf": [ + { + "additionalProperties": { + "type": "number" + }, + "propertyNames": { + "enum": [ + "top", + "right", + "bottom", + "left" + ] + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Margins Inches", + "description": "Required when margins='custom'. Each value 0.25-3.0 inches." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Custom filename (no extension). Auto-detected from first

if unset." + }, + "embed_images": { + "type": "boolean", + "title": "Embed Images", + "description": "HTML export only. When True, images are base64-embedded for offline portability (raises size cap to 150 MB). When False, images are referenced by URL (smaller file, online-only).", + "default": false + }, + "watermark_text": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Watermark Text", + "description": "PDF only. Optional text watermark overlaid on every page." + }, + "watermark_opacity": { + "type": "number", + "maximum": 1.0, + "minimum": 0.05, + "title": "Watermark Opacity", + "description": "PDF watermark opacity, 0.05-1.0.", + "default": 0.3 + } + }, + "type": "object", + "title": "ExportOptions", + "description": "User-facing export customisation (page size, orientation, margins,\nfilename, watermark). Sent on every export request." + }, + "ExportRequest": { + "properties": { + "session_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session ID to export from (the document is taken from the session). Required if html is omitted." + }, + "html": { + "anyOf": [ + { + "type": "string", + "maxLength": 200000000 + }, + { + "type": "null" + } + ], + "title": "Html", + "description": "HTML content to export inline. Used instead of the session document if provided." + }, + "format": { + "type": "string", + "enum": [ + "docx", + "pdf", + "html", + "markdown", + "txt", + "doc" + ], + "title": "Format", + "description": "Export format. One of docx (default), pdf, html, markdown, txt.", + "default": "docx" + }, + "options": { + "$ref": "#/components/schemas/ExportOptions", + "description": "Customization (paper, margins, filename, watermark, etc.)." + }, + "filename": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "DEPRECATED. Use options.filename. Top-level kept for legacy callers." + }, + "upload_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ], + "title": "Upload Id", + "description": "ID returned by /v1/uploads (large-document upload flow, for payloads above ~25 MB). When set, the route fetches the HTML body from the uploaded payload instead of using the html field." + } + }, + "type": "object", + "title": "ExportRequest", + "description": "Unified request body for POST /v1/documents/export.\n\nBackward-compat notes:\n- Top-level ``filename`` is retained for legacy clients that sent only\n ``{ html }`` and relied on the filename being auto-extracted from the\n first

. New clients should use ``options.filename`` \u2014\n options.filename wins when both are set.\n- ``format`` defaults to \"docx\". Legacy callers passing format=\"doc\"\n explicitly keep working during a short back-compat window." + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "HistoricalPromotionOut": { + "properties": { + "redemption_id": { + "type": "string", + "title": "Redemption Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + }, + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "redemption_id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at", + "status" + ], + "title": "HistoricalPromotionOut", + "description": "An exhausted, expired, or revoked promotion grant kept for the user's history view." + }, + "ImageAttachmentData": { + "properties": { + "id": { + "type": "string", + "maxLength": 256, + "title": "Id", + "description": "Unique identifier for this image." + }, + "name": { + "type": "string", + "maxLength": 512, + "title": "Name", + "description": "Original filename of the image." + }, + "base64Data": { + "type": "string", + "maxLength": 50000000, + "title": "Base64Data", + "description": "Base64-encoded image data." + }, + "mimeType": { + "type": "string", + "maxLength": 128, + "title": "Mimetype", + "description": "MIME type (e.g., 'image/png', 'image/jpeg')." + }, + "size": { + "type": "integer", + "maximum": 50000000.0, + "title": "Size", + "description": "File size in bytes." + } + }, + "type": "object", + "required": [ + "id", + "name", + "base64Data", + "mimeType", + "size" + ], + "title": "ImageAttachmentData", + "description": "Inline image attachment for vision-based analysis." + }, + "InitSessionRequest": { + "properties": { + "document_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Document Ids", + "description": "Durable document ids (from GET /v1/documents) to open into the brand-new session. Omit/empty to start an empty session." + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Optional client-chosen session id (the web UI passes its own so its session model stays consistent). Omit and the server mints one \u2014 the one-call way for an MCP/API integrator to start a session with documents already open." + } + }, + "type": "object", + "title": "InitSessionRequest" + }, + "JobListResponse": { + "properties": { + "jobs": { + "items": { + "$ref": "#/components/schemas/JobResponse" + }, + "type": "array", + "title": "Jobs", + "description": "Array of job details." + }, + "total": { + "type": "integer", + "title": "Total", + "description": "Total number of jobs returned." + } + }, + "type": "object", + "required": [ + "jobs", + "total" + ], + "title": "JobListResponse", + "description": "List of async jobs." + }, + "JobMetadata": { + "properties": { + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filename", + "description": "Uploaded filename (attachment processing jobs)." + }, + "file_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "File Size", + "description": "File size in bytes (attachment processing jobs)." + }, + "content_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content Type", + "description": "MIME type of uploaded file (attachment processing jobs)." + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Message", + "description": "Original user message (chat jobs)." + }, + "document_html_provided": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Document Html Provided", + "description": "Whether document HTML was included in the request (chat jobs)." + }, + "pending_changes": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PendingChange" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pending Changes", + "description": "Proposed changes awaiting approval. Present when job status is 'awaiting_approval'." + }, + "intermediate_responses": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Intermediate Responses", + "description": "Progress updates during processing. Each item has: type, content, sequence, timestamp." + } + }, + "additionalProperties": true, + "type": "object", + "title": "JobMetadata", + "description": "Job metadata. Contents vary by job type." + }, + "JobResponse": { + "properties": { + "job_id": { + "type": "string", + "title": "Job Id", + "description": "Unique job identifier. Use this to poll for status updates." + }, + "organization_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Organization Id", + "description": "Organization that owns this job (null for user jobs)." + }, + "user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User that owns this job (null for organization jobs)." + }, + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session associated with this job." + }, + "job_type": { + "type": "string", + "title": "Job Type", + "description": "Type of job: 'chat' or 'attachment_processing'." + }, + "status": { + "type": "string", + "title": "Status", + "description": "Current status: 'pending', 'in_progress', 'awaiting_approval', 'completed', 'failed', or 'cancelled'." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the job was created." + }, + "updated_at": { + "type": "string", + "title": "Updated At", + "description": "ISO 8601 timestamp of the last status change." + }, + "progress": { + "type": "integer", + "title": "Progress", + "description": "Progress percentage (0-100)." + }, + "result": { + "anyOf": [ + { + "$ref": "#/components/schemas/JobResult" + }, + { + "type": "null" + } + ], + "description": "Job output. Present when status is 'completed'. Contains AI response and document changes for chat jobs." + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error", + "description": "Error message. Present when status is 'failed'." + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/JobMetadata" + }, + { + "type": "null" + } + ], + "description": "Job metadata and context. Contains pending_changes when status is 'awaiting_approval'." + } + }, + "type": "object", + "required": [ + "job_id", + "session_id", + "job_type", + "status", + "created_at", + "updated_at", + "progress" + ], + "title": "JobResponse", + "description": "Status and details of an async job." + }, + "JobResult": { + "properties": { + "response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Response", + "description": "AI response text (chat jobs)." + }, + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id", + "description": "Session identifier (chat jobs)." + }, + "document_changes": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentChanges" + }, + { + "type": "null" + } + ], + "description": "Document modifications (chat jobs)." + }, + "usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/UsageInfo" + }, + { + "type": "null" + } + ], + "description": "Usage tracking data (chat jobs)." + }, + "attachment_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Attachment Id", + "description": "Processed attachment identifier (attachment processing jobs)." + } + }, + "additionalProperties": true, + "type": "object", + "title": "JobResult", + "description": "Job result data. Structure depends on job type." + }, + "LargeExportEmailRequest": { + "properties": { + "session_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Session Id" + }, + "format": { + "type": "string", + "title": "Format", + "default": "docx" + }, + "options": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Options" + }, + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filename" + }, + "recipient_email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Recipient Email" + } + }, + "type": "object", + "title": "LargeExportEmailRequest", + "description": "Body for POST /v1/documents/export/email-request.\n\nUsed when a synchronous export exceeds the inline body cap. Send the same\npayload you would POST to /v1/documents/export (minus the html field \u2014\nthat comes from the session). The export is generated in the background\nand a secure download link is emailed within 24h." + }, + "OpenDocumentsRequest": { + "properties": { + "document_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Document Ids", + "description": "Durable document ids (from GET /v1/documents) to open into the session." + } + }, + "type": "object", + "required": [ + "document_ids" + ], + "title": "OpenDocumentsRequest" + }, + "PendingChange": { + "properties": { + "change_id": { + "type": "string", + "title": "Change Id", + "description": "Unique identifier for this proposed change." + }, + "operation": { + "type": "string", + "title": "Operation", + "description": "Type of change: 'edit', 'create', or 'delete'." + }, + "chunk_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Chunk Id", + "description": "Target document section ID being modified or deleted." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Identifier of the document (tab) this change applies to \u2014 lets multi-document integrators attribute each section diff to the right document." + }, + "old_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Old Html", + "description": "Previous HTML content of the section (for updates)." + }, + "new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Html", + "description": "Proposed new HTML content (for updates and creates)." + }, + "ai_explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Explanation", + "description": "AI-generated explanation of why this change was proposed." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "change_id", + "operation" + ], + "title": "PendingChange", + "description": "A proposed document change awaiting user approval." + }, + "ProcessDocumentResponse": { + "properties": { + "html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Html" + }, + "session_id": { + "type": "string", + "title": "Session Id" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "chunks_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Chunks Count" + }, + "version_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Id" + }, + "job_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Job Id" + }, + "status": { + "type": "string", + "title": "Status" + }, + "parse_mode": { + "type": "string", + "title": "Parse Mode" + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup" + } + }, + "type": "object", + "required": [ + "session_id", + "filename", + "status", + "parse_mode" + ], + "title": "ProcessDocumentResponse" + }, + "ProcessUploadRequest": { + "properties": { + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Session ID to load this document/attachment into. Must match the chat/async session-id format \u2014 letters, digits, '_', '-', '.' only (no ':'); max 256 chars." + }, + "filename": { + "type": "string", + "maxLength": 255, + "title": "Filename", + "description": "Same filename passed to request_upload_url." + }, + "parse_mode": { + "type": "string", + "enum": [ + "document", + "attachment" + ], + "title": "Parse Mode", + "description": "'document' = parse and load as the active editable doc; 'attachment' = process asynchronously as AI-searchable reference (returns job_id to poll).", + "default": "document" + }, + "return_html": { + "type": "boolean", + "title": "Return Html", + "description": "When false (default), the response is compact \u2014 metadata only (chunks_count, version_id, page_setup), no document body \u2014 to keep your context small; the document is loaded into the session and you edit it via chat. Set true only if you actually need the parsed HTML returned inline.", + "default": false + } + }, + "type": "object", + "required": [ + "session_id", + "filename" + ], + "title": "ProcessUploadRequest" + }, + "ReEditChunkRequest": { + "properties": { + "user_current_html": { + "type": "string", + "title": "User Current Html" + }, + "ai_original_old_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Original Old Html" + }, + "ai_proposed_new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Proposed New Html" + }, + "ai_explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ai Explanation" + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier" + }, + "mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Mode", + "default": "redo" + } + }, + "type": "object", + "required": [ + "user_current_html" + ], + "title": "ReEditChunkRequest", + "description": "Re-apply the AI's intended change on the user's current section HTML, OR (mode='merge')\ncombine the user's version and the AI's version into one." + }, + "ReEditChunkResponse": { + "properties": { + "chunk_id": { + "type": "string", + "title": "Chunk Id" + }, + "new_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Html" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + } + }, + "type": "object", + "required": [ + "chunk_id" + ], + "title": "ReEditChunkResponse" + }, + "RedeemRequest": { + "properties": { + "code": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "title": "Code", + "description": "The promo code (case-insensitive)." + } + }, + "type": "object", + "required": [ + "code" + ], + "title": "RedeemRequest", + "description": "Redeem a promo code and receive its credit grant." + }, + "RedeemResponse": { + "properties": { + "status": { + "type": "string", + "title": "Status", + "description": "Always 'redeemed' on success.", + "default": "redeemed" + }, + "promotion": { + "$ref": "#/components/schemas/RedemptionOut" + }, + "message": { + "type": "string", + "title": "Message" + } + }, + "type": "object", + "required": [ + "promotion", + "message" + ], + "title": "RedeemResponse", + "description": "Response body for POST /v1/promo/redeem on success." + }, + "RedemptionOut": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "ops_granted": { + "type": "integer", + "title": "Ops Granted" + }, + "ops_remaining": { + "type": "integer", + "title": "Ops Remaining" + }, + "redeemed_at": { + "type": "string", + "title": "Redeemed At" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expires At" + } + }, + "type": "object", + "required": [ + "id", + "name", + "ops_granted", + "ops_remaining", + "redeemed_at", + "expires_at" + ], + "title": "RedemptionOut", + "description": "Summary of a successful redemption, returned from POST /v1/promo/redeem." + }, + "RedoRequest": { + "properties": { + "turn_index": { + "type": "integer", + "minimum": 0.0, + "title": "Turn Index", + "description": "The turn the revert rewound to (rows at/after this were archived); redo un-archives exactly those." + }, + "redo_checkpoint_id": { + "type": "string", + "title": "Redo Checkpoint Id", + "description": "The identifier of the pre-revert state, returned by the revert response." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window issuing the request, used to coordinate concurrent reverts from multiple browser tabs." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "turn_index", + "redo_checkpoint_id" + ], + "title": "RedoRequest", + "description": "Undo a revert \u2014 restore the captured pre-revert state and the rolled-back\nconversation + document. Meant for use immediately after a revert (before sending a new message,\nwhich would diverge the timeline); the web app gates it to that window." + }, + "RenameDocumentRequest": { + "properties": { + "title": { + "type": "string", + "maxLength": 255, + "minLength": 1, + "title": "Title", + "description": "New document title." + } + }, + "type": "object", + "required": [ + "title" + ], + "title": "RenameDocumentRequest" + }, + "RevertRequest": { + "properties": { + "turn_index": { + "type": "integer", + "minimum": 0.0, + "title": "Turn Index", + "description": "Turn index of the user message to revert. The text of that message is returned in compose_text so the caller can re-edit and resend it. Every chat row at this turn or later is soft-archived (hidden from active reads but retained for audit)." + }, + "dry_run": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Dry Run", + "description": "When true, compute the per-document revert diff WITHOUT committing or archiving anything \u2014 used to render a preview before the user confirms.", + "default": false + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window issuing the revert, used to coordinate concurrent reverts from multiple browser tabs." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "turn_index" + ], + "title": "RevertRequest", + "description": "Rewind a chat session to the state immediately before a specific user message." + }, + "RevertResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Session identifier." + }, + "reverted_to_turn": { + "type": "integer", + "title": "Reverted To Turn", + "description": "Turn index that the active conversation now ends at (the AI reply preceding the reverted user message). -1 when the session is reset to its initial empty state (revert from the very first user message)." + }, + "compose_text": { + "type": "string", + "title": "Compose Text", + "description": "The text of the user message that was reverted. Clients should pre-fill the compose box with this so the user can edit and resend." + }, + "document_state": { + "anyOf": [ + { + "$ref": "#/components/schemas/DocumentState" + }, + { + "type": "null" + } + ], + "description": "Restored document state. Null when the session is reset to empty." + }, + "editor_action": { + "type": "string", + "title": "Editor Action", + "description": "Client instruction: 'update' to load the restored document, or 'clear' to reset the editor to empty.", + "default": "update" + }, + "archived_turn_count": { + "type": "integer", + "title": "Archived Turn Count", + "description": "Number of chat rows soft-archived by this revert (turns hidden from the UI but retained for audit)." + }, + "revert_changes": { + "anyOf": [ + { + "additionalProperties": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Revert Changes", + "description": "Per-document (slug -> change-set) revert diff. Applying these merges the revert onto any concurrent live edits \u2014 a conflicting section is surfaced for you to resolve rather than replacing the whole document. On dry_run this is the preview; on a real revert it is what was committed." + }, + "dry_run": { + "type": "boolean", + "title": "Dry Run", + "description": "Echoes the request's dry_run: when true nothing was committed/archived \u2014 this is a preview only.", + "default": false + }, + "redo_checkpoint_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Redo Checkpoint Id", + "description": "The identifier of the pre-revert state. Revert is non-destructive \u2014 passing this back to POST /sessions/{id}/redo (with the same turn_index) restores it and un-archives the rolled-back turns, undoing the revert. Null on a dry-run or a first-message reset." + } + }, + "type": "object", + "required": [ + "session_id", + "reverted_to_turn", + "compose_text", + "archived_turn_count" + ], + "title": "RevertResponse", + "description": "Result of a session revert: restored document state and compose-box prefill." + }, + "SaveDocumentRequest": { + "properties": { + "html": { + "type": "string", + "title": "Html" + }, + "page_setup": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Page Setup" + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id" + }, + "base_html": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Html" + } + }, + "type": "object", + "required": [ + "html" + ], + "title": "SaveDocumentRequest", + "description": "Body for the human-edit autosave." + }, + "SessionInfo": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Unique session identifier." + }, + "user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User who owns this session (null for organization sessions)." + }, + "created_at": { + "type": "string", + "title": "Created At", + "description": "ISO 8601 timestamp when the session was created." + }, + "last_activity": { + "type": "string", + "title": "Last Activity", + "description": "ISO 8601 timestamp of the most recent activity." + }, + "message_count": { + "type": "integer", + "title": "Message Count", + "description": "Total number of messages in this session." + }, + "preview": { + "type": "string", + "title": "Preview", + "description": "Preview of the first user message (up to 100 characters)." + } + }, + "type": "object", + "required": [ + "session_id", + "created_at", + "last_activity", + "message_count", + "preview" + ], + "title": "SessionInfo", + "description": "Summary of a document editing session." + }, + "SessionListResponse": { + "properties": { + "sessions": { + "items": { + "$ref": "#/components/schemas/SessionInfo" + }, + "type": "array", + "title": "Sessions", + "description": "Array of session summaries, ordered by most recent activity." + }, + "total": { + "type": "integer", + "title": "Total", + "description": "Total number of sessions returned." + } + }, + "type": "object", + "required": [ + "sessions", + "total" + ], + "title": "SessionListResponse", + "description": "List of document editing sessions." + }, + "UniversalChatRequest": { + "properties": { + "message": { + "type": "string", + "maxLength": 100000, + "title": "Message", + "description": "Your message to the AI assistant." + }, + "session_id": { + "type": "string", + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-\\.]+$", + "title": "Session Id", + "description": "Unique session identifier. Reuse to continue a conversation." + }, + "document_html": { + "anyOf": [ + { + "type": "string", + "maxLength": 50000000 + }, + { + "type": "null" + } + ], + "title": "Document Html", + "description": "Current document HTML. OMIT this when the session already holds the document \u2014 the server persists it across turns, so you don't re-send it every turn; pass it only to load new content or replace the document wholesale. Include data-chunk-id attributes on elements to enable targeted AI edits." + }, + "user_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "User Id", + "description": "User identifier. Automatically set from authentication \u2014 typically omit this." + }, + "image_attachments": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ImageAttachmentData" + }, + "type": "array", + "maxItems": 20 + }, + { + "type": "null" + } + ], + "title": "Image Attachments", + "description": "Inline images for vision-based analysis (base64-encoded)." + }, + "model_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Tier", + "description": "AI model to use: 'core' (default, fast), 'turbo' (fastest), 'pro' (advanced reasoning), 'max' (most capable)." + }, + "thinking_depth": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Thinking Depth", + "description": "Reasoning depth: 'fast', 'balanced' (default), or 'deep'. Controls how much analysis the AI performs." + }, + "approval_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Approval Mode", + "description": "Change review mode: 'approve_all' (default, auto-applies changes) or 'ask_every_time' (pauses for your review)." + }, + "response_mode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "full", + "compact" + ] + }, + { + "type": "null" + } + ], + "title": "Response Mode", + "description": "Response shape control. 'full' (default) returns the complete updated document HTML \u2014 required by web app editors and recommended for small documents (<20 pages). 'compact' (recommended for AI agents editing large documents) suppresses the full HTML and returns only per-section diffs (chunk_diffs) for changed sections, saving thousands of tokens per turn. To read sections in compact mode, just send a natural-language request like 'show me the force majeure section' \u2014 the AI returns the content in the chat reply text.", + "default": "full" + }, + "cursor_context": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Cursor Context", + "description": "Editor cursor context at message time, used to default the insert location for new images, diagrams, or sections when the user's prompt doesn't name a position. Shape: {chunk_id: str, pos_in_chunk?: int, text_before?: str, text_after?: str}. Omit when the user typed in chat without focusing the editor first." + }, + "document_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Id", + "description": "Target a specific open document by id (ids come from /v1/sessions/{session_id}/documents). Omit to target the focused document (default \u2014 unchanged single-document behaviour). When set, that document becomes the focus for this turn." + }, + "window_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Window Id", + "description": "Optional identifier for the client window (set by the web app) so concurrent edits from two windows of the same chat are merged rather than overwritten. Agents/API may omit." + }, + "cross_session_memory": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory", + "description": "When true, the AI carries a private rolling memory of context across THIS account's chats (off by default). Per-request override of your saved Settings default." + }, + "cross_session_search": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cross Session Search", + "description": "When true, the AI may search your PAST chats and documents on demand to reuse prior work and open a found document (off by default). Per-request override of your saved Settings default; never affects the always-on search of the open document." + }, + "cross_session_memory_key": { + "anyOf": [ + { + "type": "string", + "maxLength": 256 + }, + { + "type": "null" + } + ], + "title": "Cross Session Memory Key", + "description": "Optional per-request id of YOUR end-customer so the cross-session memory note is kept in a separate file per end-customer. Omit for one account-level note (the default / B2C)." + }, + "cross_session_scope": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 2000 + }, + { + "type": "null" + } + ], + "title": "Cross Session Scope", + "description": "Optional list of session ids to restrict cross-session SEARCH to (e.g. one end-customer's sessions). Omit to search all of this account's sessions. Only narrows within the API-key owner; ignored unless cross_session_search is on." + } + }, + "type": "object", + "required": [ + "message", + "session_id" + ], + "title": "UniversalChatRequest", + "description": "Send a message to the AI assistant with optional document context.", + "examples": [ + { + "approval_mode": "approve_all", + "document_html": "

Section 3

Terms and conditions...

", + "message": "Add a confidentiality clause to section 3", + "model_tier": "core", + "session_id": "session_abc123" + } + ] + }, + "UpdateProfileRequest": { + "properties": { + "display_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Display Name", + "description": "New display name." + }, + "timezone": { + "anyOf": [ + { + "type": "string", + "maxLength": 100 + }, + { + "type": "null" + } + ], + "title": "Timezone", + "description": "IANA timezone (e.g., 'America/New_York')." + }, + "language": { + "anyOf": [ + { + "type": "string", + "maxLength": 10 + }, + { + "type": "null" + } + ], + "title": "Language", + "description": "Preferred language code (e.g., 'en', 'es')." + }, + "preferences": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Preferences", + "description": "User preferences to update (e.g., {approval_mode: 'ask_every_time', model_tier: 'pro'})." + } + }, + "type": "object", + "title": "UpdateProfileRequest", + "description": "Update user profile fields." + }, + "UploadUrlRequest": { + "properties": { + "filename": { + "type": "string", + "maxLength": 255, + "title": "Filename", + "description": "Original filename including extension (e.g. 'contract.docx'). Used downstream for file-type detection." + }, + "content_type": { + "type": "string", + "maxLength": 200, + "title": "Content Type", + "description": "MIME type the agent will PUT with (e.g. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' for .docx)." + }, + "size_bytes": { + "type": "integer", + "maximum": 104857600.0, + "exclusiveMinimum": 0.0, + "title": "Size Bytes", + "description": "File size in bytes. Must be > 0 and <= 104857600 (100 MB)." + }, + "purpose": { + "type": "string", + "enum": [ + "document", + "attachment", + "export-html" + ], + "title": "Purpose", + "description": "What this file will be used for. 'document' = the active editable doc; 'attachment' = read-only AI-searchable reference; 'export-html' = HTML payload destined for /v1/documents/export (large-export upload flow, for documents above ~25 MB).", + "default": "document" + } + }, + "type": "object", + "required": [ + "filename", + "content_type", + "size_bytes" + ], + "title": "UploadUrlRequest" + }, + "UploadUrlResponse": { + "properties": { + "upload_id": { + "type": "string", + "title": "Upload Id" + }, + "upload_url": { + "type": "string", + "title": "Upload Url" + }, + "expires_at": { + "type": "string", + "title": "Expires At" + }, + "expires_in_seconds": { + "type": "integer", + "title": "Expires In Seconds" + }, + "max_size_bytes": { + "type": "integer", + "title": "Max Size Bytes" + }, + "curl_example": { + "type": "string", + "title": "Curl Example" + } + }, + "type": "object", + "required": [ + "upload_id", + "upload_url", + "expires_at", + "expires_in_seconds", + "max_size_bytes", + "curl_example" + ], + "title": "UploadUrlResponse" + }, + "UsageInfo": { + "properties": { + "monthly_used": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Used", + "description": "Total operations used this billing cycle." + }, + "monthly_limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Limit", + "description": "Maximum operations allowed per cycle. -1 means unlimited." + }, + "monthly_remaining": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Monthly Remaining", + "description": "Operations remaining this cycle. -1 means unlimited." + }, + "was_billable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Was Billable", + "description": "Whether this request counted as a billable operation." + }, + "ops_charged": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Ops Charged", + "description": "How many operations this request billed. A large multi-section edit can bill more than one (one per 25 sections edited), so monthly_used can increase by more than 1 between responses." + }, + "quota_exhausted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Quota Exhausted", + "description": "True when you have reached your plan's operation limit with no remaining balance \u2014 the current request still completes, but further billable requests pause until you upgrade or your billing cycle resets." + }, + "subscription_tier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Subscription Tier", + "description": "Current subscription tier: 'free', 'plus', 'pro', or 'enterprise'." + }, + "bucket_used": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bucket Used", + "description": "Which bucket this operation drew from: 'tier' or 'promo'. Null for non-billable ops." + }, + "redemption_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Redemption Id", + "description": "Redemption grant this op drew from, when bucket_used is 'promo'." + }, + "promotions": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ActivePromotionInfo" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Promotions", + "description": "User's currently-active promotion grants, ordered by oldest-expiring first." + } + }, + "additionalProperties": true, + "type": "object", + "title": "UsageInfo", + "description": "Operation usage data for the current billing cycle." + }, + "UsageStatsResponse": { + "properties": { + "user_id": { + "type": "string", + "title": "User Id", + "description": "User identifier." + }, + "subscription_tier": { + "type": "string", + "title": "Subscription Tier", + "description": "Current plan: 'free', 'plus', 'pro', or 'enterprise'." + }, + "monthly_limit": { + "type": "integer", + "title": "Monthly Limit", + "description": "Maximum operations allowed per month. -1 means unlimited." + }, + "monthly_used": { + "type": "integer", + "title": "Monthly Used", + "description": "Operations used this billing cycle." + }, + "monthly_remaining": { + "type": "integer", + "title": "Monthly Remaining", + "description": "Operations remaining this cycle." + }, + "monthly_reset_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Monthly Reset At", + "description": "ISO 8601 timestamp when the monthly counter resets." + }, + "total_sessions": { + "type": "integer", + "title": "Total Sessions", + "description": "Total number of chat sessions created." + }, + "total_documents": { + "type": "integer", + "title": "Total Documents", + "description": "Total number of documents processed." + }, + "total_operations": { + "type": "integer", + "title": "Total Operations", + "description": "Lifetime total operations across all billing cycles." + }, + "current_month_stats": { + "additionalProperties": true, + "type": "object", + "title": "Current Month Stats", + "description": "Breakdown of this month's operations by type, including counts, tokens used, and success rates." + } + }, + "type": "object", + "required": [ + "user_id", + "subscription_tier", + "monthly_limit", + "monthly_used", + "monthly_remaining", + "total_sessions", + "total_documents", + "total_operations", + "current_month_stats" + ], + "title": "UsageStatsResponse", + "description": "Detailed usage statistics for the current billing cycle." + }, + "UserProfileResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Unique user identifier (UUID)." + }, + "email": { + "type": "string", + "title": "Email", + "description": "User's email address." + }, + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Display Name", + "description": "User's display name." + }, + "photo_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Photo Url", + "description": "URL to user's profile photo." + }, + "email_verified": { + "type": "boolean", + "title": "Email Verified", + "description": "Whether the user's email has been verified." + }, + "auth_provider": { + "type": "string", + "title": "Auth Provider", + "description": "Authentication method: 'email' or 'google'." + }, + "subscription_tier": { + "type": "string", + "title": "Subscription Tier", + "description": "Current plan: 'free', 'plus', 'pro', or 'enterprise'." + }, + "subscription_status": { + "type": "string", + "title": "Subscription Status", + "description": "Subscription status: 'active', 'canceled', or 'past_due'." + }, + "monthly_operation_limit": { + "type": "integer", + "title": "Monthly Operation Limit", + "description": "Maximum operations allowed per month. -1 means unlimited." + }, + "monthly_operations_used": { + "type": "integer", + "title": "Monthly Operations Used", + "description": "Operations used this billing cycle." + }, + "monthly_remaining": { + "type": "integer", + "title": "Monthly Remaining", + "description": "Operations remaining this cycle." + }, + "preferences": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Preferences", + "description": "User preferences (e.g., approval_mode, model_tier)." + }, + "created_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At", + "description": "ISO 8601 timestamp when the account was created." + }, + "last_login_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Login At", + "description": "ISO 8601 timestamp of the last login." + } + }, + "type": "object", + "required": [ + "id", + "email", + "email_verified", + "auth_provider", + "subscription_tier", + "subscription_status", + "monthly_operation_limit", + "monthly_operations_used", + "monthly_remaining" + ], + "title": "UserProfileResponse", + "description": "User profile with subscription and usage information." + }, + "UserPromotionsResponse": { + "properties": { + "active": { + "items": { + "$ref": "#/components/schemas/ActivePromotionOut" + }, + "type": "array", + "title": "Active" + }, + "history": { + "items": { + "$ref": "#/components/schemas/HistoricalPromotionOut" + }, + "type": "array", + "title": "History" + } + }, + "type": "object", + "required": [ + "active", + "history" + ], + "title": "UserPromotionsResponse", + "description": "Listing of the authenticated user's promotion grants, split by active vs. history." + }, + "UserSessionResponse": { + "properties": { + "session_id": { + "type": "string", + "title": "Session Id", + "description": "Unique session identifier." + }, + "last_message_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Message At", + "description": "ISO 8601 timestamp of the most recent message." + }, + "message_count": { + "type": "integer", + "title": "Message Count", + "description": "Total number of messages in this session." + } + }, + "type": "object", + "required": [ + "session_id", + "message_count" + ], + "title": "UserSessionResponse", + "description": "Summary of a user's chat session." + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + }, + "securitySchemes": { + "HTTPBearer": { + "type": "http", + "scheme": "bearer" + } + } + } +} \ No newline at end of file diff --git a/extensions/doctask2-oviya-senthilkumar/tests/python/test_smoke.py b/extensions/doctask2-oviya-senthilkumar/tests/python/test_smoke.py new file mode 100644 index 00000000..f12df6bb --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/tests/python/test_smoke.py @@ -0,0 +1,28 @@ +""" +Smoke test: drive the GENERATED Python client against the Prism mock server. +Proves the generated client is usable end-to-end — it builds a request from the +spec, calls the mock, and deserializes the response into the generated model. +No API key, no live service — Prism serves schema-valid examples. + +Run: pytest tests/python (with Prism mock up on SUPERDOCS_MOCK_URL) +""" +import os +import pytest + +from superdocs_client import Configuration, ApiClient +from superdocs_client.api.users_api import UsersApi + +MOCK_URL = os.environ.get("SUPERDOCS_MOCK_URL", "http://localhost:4010") + + +def test_get_current_user_profile_against_mock(): + cfg = Configuration(host=MOCK_URL, access_token="smoke-test-token") + with ApiClient(cfg) as api_client: + users = UsersApi(api_client) + # GET /v1/users/me — a representative typed call through the client. + profile = users.get_current_user_profile_v1_users_me_get() + + assert profile is not None + # Prism returned a schema-valid example, so the generated model populated. + assert hasattr(profile, "email"), "expected a typed user profile model" + print(f"\nPython smoke OK — {MOCK_URL}/v1/users/me -> {type(profile).__name__}") diff --git a/extensions/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts b/extensions/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts new file mode 100644 index 00000000..aa4b2ae8 --- /dev/null +++ b/extensions/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts @@ -0,0 +1,35 @@ +/** + * Smoke test: drive the GENERATED TypeScript (axios) client against the Prism + * mock. Proves the generated client is usable end-to-end — it builds the request + * from the spec, calls the mock, and returns a typed UserProfileResponse. + * No API key, no live service. + * + * Run: npx tsx tests/typescript/smoke.ts (with Prism mock up) + */ +import { Configuration } from "../../clients/typescript/configuration"; +import { UsersApi } from "../../clients/typescript/api/users-api"; + +const MOCK_URL = process.env.SUPERDOCS_MOCK_URL || "http://localhost:4010"; + +async function main() { + const cfg = new Configuration({ basePath: MOCK_URL, accessToken: "smoke-test-token" }); + const users = new UsersApi(cfg); + + // GET /v1/users/me — a representative typed call through the client. + const res = await users.getCurrentUserProfileV1UsersMeGet(); + + if (res.status !== 200) { + throw new Error(`expected HTTP 200, got ${res.status}`); + } + if (!res.data || typeof (res.data as any).email === "undefined") { + throw new Error("expected a typed UserProfileResponse with an `email` field"); + } + console.log( + `TypeScript smoke OK — ${MOCK_URL}/v1/users/me -> ${res.status}, typed body has .email` + ); +} + +main().catch((e) => { + console.error("TS smoke FAILED:", e.message); + process.exit(1); +}); From ee2dcb44f664eeced34204956df15405eb573bb3 Mon Sep 17 00:00:00 2001 From: Oviya Date: Thu, 20 Aug 2026 14:52:08 +0530 Subject: [PATCH 2/2] Move task2 under extensions/oviya12/ per contributing guidelines Contributing guide requires extensions///. Relocated extensions/doctask2-oviya-senthilkumar/ -> extensions/oviya12/doctask2-oviya-senthilkumar/. --- .../{ => oviya12}/doctask2-oviya-senthilkumar/.gitattributes | 0 .../doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml | 0 extensions/{ => oviya12}/doctask2-oviya-senthilkumar/.gitignore | 0 extensions/{ => oviya12}/doctask2-oviya-senthilkumar/Makefile | 0 extensions/{ => oviya12}/doctask2-oviya-senthilkumar/README.md | 0 .../{ => oviya12}/doctask2-oviya-senthilkumar/mock/README.md | 0 .../{ => oviya12}/doctask2-oviya-senthilkumar/package-lock.json | 0 extensions/{ => oviya12}/doctask2-oviya-senthilkumar/package.json | 0 .../doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json | 0 .../doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md | 0 .../{ => oviya12}/doctask2-oviya-senthilkumar/reports/compat.json | 0 .../{ => oviya12}/doctask2-oviya-senthilkumar/reports/compat.md | 0 .../{ => oviya12}/doctask2-oviya-senthilkumar/scripts/compat.py | 0 .../{ => oviya12}/doctask2-oviya-senthilkumar/scripts/fetch.sh | 0 .../doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py | 0 .../{ => oviya12}/doctask2-oviya-senthilkumar/scripts/pipeline.sh | 0 .../doctask2-oviya-senthilkumar/specs/superdocs-breaking.json | 0 .../doctask2-oviya-senthilkumar/specs/superdocs-new.json | 0 .../doctask2-oviya-senthilkumar/specs/superdocs-old.json | 0 .../doctask2-oviya-senthilkumar/tests/python/test_smoke.py | 0 .../doctask2-oviya-senthilkumar/tests/typescript/smoke.ts | 0 21 files changed, 0 insertions(+), 0 deletions(-) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/.gitattributes (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/.gitignore (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/Makefile (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/README.md (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/mock/README.md (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/package-lock.json (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/package.json (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/reports/compat.json (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/reports/compat.md (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/scripts/compat.py (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/scripts/fetch.sh (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/scripts/pipeline.sh (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/specs/superdocs-new.json (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/specs/superdocs-old.json (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/tests/python/test_smoke.py (100%) rename extensions/{ => oviya12}/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts (100%) diff --git a/extensions/doctask2-oviya-senthilkumar/.gitattributes b/extensions/oviya12/doctask2-oviya-senthilkumar/.gitattributes similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/.gitattributes rename to extensions/oviya12/doctask2-oviya-senthilkumar/.gitattributes diff --git a/extensions/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml b/extensions/oviya12/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml rename to extensions/oviya12/doctask2-oviya-senthilkumar/.github/workflows/pipeline.yml diff --git a/extensions/doctask2-oviya-senthilkumar/.gitignore b/extensions/oviya12/doctask2-oviya-senthilkumar/.gitignore similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/.gitignore rename to extensions/oviya12/doctask2-oviya-senthilkumar/.gitignore diff --git a/extensions/doctask2-oviya-senthilkumar/Makefile b/extensions/oviya12/doctask2-oviya-senthilkumar/Makefile similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/Makefile rename to extensions/oviya12/doctask2-oviya-senthilkumar/Makefile diff --git a/extensions/doctask2-oviya-senthilkumar/README.md b/extensions/oviya12/doctask2-oviya-senthilkumar/README.md similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/README.md rename to extensions/oviya12/doctask2-oviya-senthilkumar/README.md diff --git a/extensions/doctask2-oviya-senthilkumar/mock/README.md b/extensions/oviya12/doctask2-oviya-senthilkumar/mock/README.md similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/mock/README.md rename to extensions/oviya12/doctask2-oviya-senthilkumar/mock/README.md diff --git a/extensions/doctask2-oviya-senthilkumar/package-lock.json b/extensions/oviya12/doctask2-oviya-senthilkumar/package-lock.json similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/package-lock.json rename to extensions/oviya12/doctask2-oviya-senthilkumar/package-lock.json diff --git a/extensions/doctask2-oviya-senthilkumar/package.json b/extensions/oviya12/doctask2-oviya-senthilkumar/package.json similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/package.json rename to extensions/oviya12/doctask2-oviya-senthilkumar/package.json diff --git a/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json b/extensions/oviya12/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json rename to extensions/oviya12/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.json diff --git a/extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md b/extensions/oviya12/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md rename to extensions/oviya12/doctask2-oviya-senthilkumar/reports/breaking-demo/compat.md diff --git a/extensions/doctask2-oviya-senthilkumar/reports/compat.json b/extensions/oviya12/doctask2-oviya-senthilkumar/reports/compat.json similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/reports/compat.json rename to extensions/oviya12/doctask2-oviya-senthilkumar/reports/compat.json diff --git a/extensions/doctask2-oviya-senthilkumar/reports/compat.md b/extensions/oviya12/doctask2-oviya-senthilkumar/reports/compat.md similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/reports/compat.md rename to extensions/oviya12/doctask2-oviya-senthilkumar/reports/compat.md diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/compat.py b/extensions/oviya12/doctask2-oviya-senthilkumar/scripts/compat.py similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/scripts/compat.py rename to extensions/oviya12/doctask2-oviya-senthilkumar/scripts/compat.py diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/fetch.sh b/extensions/oviya12/doctask2-oviya-senthilkumar/scripts/fetch.sh similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/scripts/fetch.sh rename to extensions/oviya12/doctask2-oviya-senthilkumar/scripts/fetch.sh diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py b/extensions/oviya12/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py rename to extensions/oviya12/doctask2-oviya-senthilkumar/scripts/make_breaking_demo.py diff --git a/extensions/doctask2-oviya-senthilkumar/scripts/pipeline.sh b/extensions/oviya12/doctask2-oviya-senthilkumar/scripts/pipeline.sh similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/scripts/pipeline.sh rename to extensions/oviya12/doctask2-oviya-senthilkumar/scripts/pipeline.sh diff --git a/extensions/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json b/extensions/oviya12/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json rename to extensions/oviya12/doctask2-oviya-senthilkumar/specs/superdocs-breaking.json diff --git a/extensions/doctask2-oviya-senthilkumar/specs/superdocs-new.json b/extensions/oviya12/doctask2-oviya-senthilkumar/specs/superdocs-new.json similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/specs/superdocs-new.json rename to extensions/oviya12/doctask2-oviya-senthilkumar/specs/superdocs-new.json diff --git a/extensions/doctask2-oviya-senthilkumar/specs/superdocs-old.json b/extensions/oviya12/doctask2-oviya-senthilkumar/specs/superdocs-old.json similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/specs/superdocs-old.json rename to extensions/oviya12/doctask2-oviya-senthilkumar/specs/superdocs-old.json diff --git a/extensions/doctask2-oviya-senthilkumar/tests/python/test_smoke.py b/extensions/oviya12/doctask2-oviya-senthilkumar/tests/python/test_smoke.py similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/tests/python/test_smoke.py rename to extensions/oviya12/doctask2-oviya-senthilkumar/tests/python/test_smoke.py diff --git a/extensions/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts b/extensions/oviya12/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts similarity index 100% rename from extensions/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts rename to extensions/oviya12/doctask2-oviya-senthilkumar/tests/typescript/smoke.ts