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

# Migrate from Documents Classic

> Concept-by-concept mapping from POST /interactions/{id}/documents (Classic) to POST /documents and the new /documents/sections + /documents/templates API

<Badge className="accent-badge" shape="rounded">Beta</Badge>

<Tip>
  **Classic continues to work today** and be deprecated as soon as the Guided Documents Beta surface moves into general availability. The exact sunset timeline will be communicated via the [upcoming changes log](/release-notes/changelog-upcoming).

  New integrations should build on the Beta surface (`POST /documents` + the `/documents/sections` and `/documents/templates` authoring API). Existing Classic integrations should plan their migration using this guide.
</Tip>

## Guided Documents (beta) vs. Classic at a glance

<Columns cols={2}>
  <Card title="Guided Documents" icon="wand-sparkles" href="/textgen/documents-guided-synthesis">
    **Endpoint:** `POST /documents`

    * Sections & templates as **UUID resources**, versioned + publishable
    * Authoring API: `/documents/sections`, `/documents/templates`
    * Typed `outputSchema` for structured outputs
    * `inheritFromId` for variants
    * Interaction-**optional**
    * Source-aware: `source: corti` Standards interleave with `source: user`

    Start with [Guided Synthesis](/textgen/documents-guided-synthesis) or the [Customization Cookbook](/textgen/customization-cookbook).
  </Card>

  <Card title="Documents Classic" icon="file-text" href="/textgen/documents-standard">
    **Endpoint:** `POST /interactions/{id}/documents`

    * String `templateKey` (`corti-soap`, `corti-hpi`, …)
    * Read-only Corti library + per-request inline overrides
    * Free-text `formatRuleOverride`
    * No authoring, no versioning
    * Interaction-**scoped**

    **Planned for deprecation when beta moves to general availability.**
  </Card>
</Columns>

## Why migrate

**The Guided surface is the path forward.** Documents Classic will be deprecated once Beta reaches general availability — see the callout above. Migrating before the sunset gives you time to validate parity and gain the new capabilities.

Documents Classic uses **string keys** (`templateKey: "corti-soap"`, `key: "corti-hpi"`) and a fixed override surface (`nameOverride`, `writingStyleOverride`, `formatRuleOverride`, `additionalInstructionsOverride`, `contentOverride`) attached to `POST /interactions/{id}/documents`.

The new API treats **sections and templates as first-class, UUID-addressable, versioned resources** and moves generation to `POST /documents` — interaction-optional, with a single consistent request shape across four template-supply paths. Concretely you gain:

* **Typed, schema-controlled outputs** (`outputSchema`) instead of free-text formatting rules — see [Section Schemas](/textgen/section-schemas).
* **Versioning & publish lifecycle** for your own sections and templates — see [Create a Section](/textgen/section-creation).
* **Drift-proof generation snapshots** — every call records the resolved template aggregate it used.
* **Interaction-optional context** — pass explicit `context` (text / transcript / facts) **or** an `interactionId` — they are currently mutually exclusive; combining is on the roadmap.

<Tip>**SDK helpers for the migration are planned.** Until they land, this guide walks you through the mapping manually. The mapping is mostly mechanical — most of the work is one-time discovery (finding the right Standard UUIDs for your locales).</Tip>

## Mental-model shift

Three things change about how you think about templates and sections:

1. **String keys → UUIDs.** `templateKey: "corti-soap"` becomes `templateId: "<some-uuid>"`. Section keys behave the same way.
2. **Per-locale UUIDs.** Each Corti Standard section and template is published as a **separate resource per locale**. The English `corti-hpi` and the German `corti-hpi` share the same `key` but have **different UUIDs**. Your discovery step must include the `lang` filter.
3. **Source-aware library.** `LIST /documents/sections` and `LIST /documents/templates` return your own resources alongside Corti Standards. Each item carries a `source` field — `corti` for Corti Standards, `user` for API-client–authored resources in your project — so you can filter client-side. Server-side query params today are `lang`, `region`, `specialty`, `label` and `published`. See [Corti Standards](/textgen/corti-standards).

## Endpoint mapping

| Classic                                                                        | New                                                                                                                                                        |
| :----------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /templates`                                                               | `LIST /documents/templates` *(filter via `lang`, `region`, `specialty`, `label`; filter on `source` client-side to scope to Corti Standards vs. your own)* |
| `GET /templates/{key}`                                                         | `GET /documents/templates/{templateID}` *(after resolving the key + locale to a UUID via the list endpoint)*                                               |
| `GET /templateSections`                                                        | `LIST /documents/sections` *(same filtering)*                                                                                                              |
| `POST /interactions/{id}/documents` *(with `templateKey`)*                     | `POST /documents` — **Path 1** *(plain `templateRef`)*                                                                                                     |
| `POST /interactions/{id}/documents` *(with `template.sections[]` + overrides)* | `POST /documents` — **Path 2** *(`templateRef` + `overrides`)* or **Path 3** *(`assemblyTemplate`)* — see below                                            |

## Field mapping — Classic overrides → new

Where a Classic override sat under `template.sections[].keyOverride`, the new equivalent lives under `overrides.sections[].generation.*` on a `templateRef` or `assemblyTemplate` request.

| Classic field                                                         | New equivalent                                                                              | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| :-------------------------------------------------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `template.description`                                                | `overrides.instructions.prompt` (Path 2) or `assemblyTemplate.instructions.prompt` (Path 3) | Template-level prompt                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `template.additionalInstructionsOverride`                             | `overrides.instructions.prompt`                                                             | Template-level                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `sections[].key`                                                      | `sections[].sectionId` (Path 2) / `sectionRefs[].sectionId` (Path 3)                        | Per-locale UUID — resolve once via `LIST /documents/sections?lang=...` (filter on `source: "corti"` client-side if scoping to Standards)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `sections[].nameOverride`                                             | `sections[].generation.heading`                                                             | Section heading override                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `sections[].writingStyleOverride`                                     | `sections[].generation.instructions.writingStylePrompt`                                     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `sections[].contentOverride`                                          | `sections[].generation.instructions.contentPrompt`                                          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `sections[].additionalInstructionsOverride`                           | `sections[].generation.instructions.miscPrompt`                                             | Free-form section-level guidance                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `sections[].formatRuleOverride`                                       | **No 1:1 — use `outputSchema`**                                                             | See note below                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `outputLanguage`                                                      | `outputLanguage`                                                                            | Unchanged                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `name`                                                                | n/a on the request — track in your own system if needed                                     | The response carries the auto-generated `templateId` you can use to identify the call                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `context: [{ type: "facts" \| "transcript" \| "string", data: ... }]` | `context: [{ type: "text" \| "transcript" \| "facts", ... }]` *or* `interactionId`          | **Context input is more flexible in the new API.** Classic accepts **exactly one** `context.type` per call — facts *or* transcript *or* string, never mixed. The Beta surface lets you **combine context types in a single `context` array** (e.g. a referral letter as `text` + a transcript + a `facts` item in one call). Alternatively, supply **`interactionId`** to pull all non-discarded facts and transcripts attached to that interaction implicitly — no manual context wiring. `context` and `interactionId` are **mutually exclusive today** (combining them is on the roadmap). For facts: use `type: "facts"` with a **`facts` array** of `{ text, group? }` objects (not Classic's `data[]`). See [Input context](/textgen/documents-guided-synthesis#input-context). |

### About `formatRuleOverride`

Classic used `formatRuleOverride` as a free-text format instruction (e.g. *"single paragraph in running text"* or *"use fixed subheadings: **Diagnosis**:\[content]\n\n**Assessment**:\[content]"*). The new API replaces this with **typed `outputSchema` formatting primitives**:

* `array.itemFormat` — format string that must contain the `{item}` placeholder (e.g. `"- {item}\n"`, `"* {item}\n"`, `"Rp. {item}\n"`)
* `object.fieldFormat` — per-field iteration template using generic `{key}` / `{value}` placeholders (e.g. `"{key}: {value}\n"` inline, `"{key}\n{value}\n"` block), **or** a fully custom layout referencing specific field keys (e.g. `"{test}: {result}"`)

This is more powerful but not a drop-in replacement. Two migration paths:

1. **Inherit from the section** with a new `outputSchema` (Recipe 2 of the [Customization Cookbook](/textgen/customization-cookbook)). One-time setup; persistent.
2. **Override `outputSchema` at runtime** on the guided-synthesis call. Note that `outputSchema` overrides are **wholesale** — you must submit the full schema, not a partial. See [Customization Cookbook — override semantics](/textgen/customization-cookbook#override-semantics-the-cheat-sheet).

See [Section Schemas](/textgen/section-schemas) for design patterns that cover most Classic `formatRuleOverride` use cases.

## Discovery — resolve Classic keys to UUIDs

Migration starts with a one-time discovery step: for each `(key, locale)` pair you use in Classic, find the corresponding UUID in the new API.

<CodeGroup>
  ```ts title="JavaScript" theme={null}
  // Filter by language, region, specialty, label, and/or publish status.
  // All four filter params are repeatable arrays. `label` values use "key:value".
  const sections = await client.documents.sections.list({
    lang: ["en"],
    region: ["USA"],
    specialty: ["dermatology"],
    label: ["customer:acme"],
    published: true,
  });
  ```

  ```csharp title="C# .NET" theme={null}
  using Corti;

  var sections = await client.Documents.Sections.ListAsync(
      new GuidedSectionsListRequest
      {
          Lang = new[] { "en" },
          Region = new[] { "USA" },
          Specialty = new[] { "dermatology" },
          Label = new[] { "customer:acme" },
          Published = true,
      });
  ```

  ```python title="Python" theme={null}
  import requests

  # Replace these with your values
  ENVIRONMENT = "<eu-or-us>"
  TENANT = "<your-tenant-name>"
  TOKEN = "<your-access-token>"

  url = f"https://api.{ENVIRONMENT}.corti.app/v2/documents/sections"
  headers = {
      "Authorization": f"Bearer {TOKEN}",
      "Tenant-Name": TENANT,
  }

  # `lang`, `region`, `specialty`, and `label` are repeatable; `label` uses "key:value".
  params = [
      ("lang", "en"),
      ("region", "USA"),
      ("specialty", "dermatology"),
      ("label", "customer:acme"),
      ("published", "true"),
  ]
  response = requests.get(url, headers=headers, params=params)
  response.raise_for_status()
  sections = response.json()
  ```

  ```bash title="cURL" theme={null}
  # Replace these with your values
  ENVIRONMENT="<eu-or-us>"
  TENANT="<your-tenant-name>"
  TOKEN="<your-access-token>"

  # `lang`, `region`, `specialty`, and `label` are repeatable; `label` uses "key:value".
  curl "https://api.${ENVIRONMENT}.corti.app/v2/documents/sections?lang=en&region=USA&specialty=dermatology&label=customer:acme&published=true" \
    -H "Authorization: Bearer ${TOKEN}" \
    -H "Tenant-Name: ${TENANT}"
  ```
</CodeGroup>

The response includes each section's `id`, `key`, `languages`, `regions`, and `specialties` (list items omit `publishedVersion` — call `GET /documents/sections/{sectionID}` when you need it). Build a `(key, locale [, region, specialty]) → sectionId` map in your service. Do the same for templates via `LIST /documents/templates?lang=...`. If you want to scope to Corti Standards only, filter the response on `source === "corti"` client-side.

<Tip>Cache this mapping in your service. Corti Standard UUIDs are stable for the lifetime of a resource (only the underlying published version evolves silently — see [Corti Standards limitations](/textgen/corti-standards#limitations-and-guarantees)).</Tip>

## Step-by-step migration

<Steps titleSize="h4">
  <Step title="Inventory your Classic usage">
    For each `POST /interactions/{id}/documents` call in your codebase, note:

    * which `templateKey` (or inline `template.sections[]`) you use,
    * which `outputLanguage` values you support,
    * which override fields are populated and whether they're constant per integration or vary per request,
    * any use of `documentationMode`, `disableGuardrails` or the `X-Corti-Retention-Policy` header.
  </Step>

  <Step title="Build the key → UUID map">
    For every `(Corti Standard key, locale)` pair from step 1, call `LIST /documents/sections?lang=...` and `LIST /documents/templates?lang=...` (filter on `source === "corti"` client-side) and persist the mapping in your service. If your Classic integration used region- or specialty-specific variants, also pass `region` and `specialty` filters.
  </Step>

  <Step title="Pick a path per call site">
    * If you used `templateKey` with **no overrides** → **Path 1** (plain `templateRef`).
    * If you used `templateKey` **with overrides** *or* inline `template.sections[]` that maps cleanly onto an existing template → **Path 2** (`templateRef` + `overrides`).
    * If you assembled sections inline at request time (no stored template) → **Path 3** (`assemblyTemplate`).
    * If you want to start fully fresh with inline section definitions → **Path 4** (`dynamicTemplate`).
    * See [Guided Synthesis — the four paths](/textgen/documents-guided-synthesis#the-four-template-supply-paths).
  </Step>

  <Step title="Translate the request">
    Apply the field-mapping table above. The biggest change is moving from `template.sections[].key` + `*Override` to `overrides.sections[].sectionId` + `generation.*`. See the side-by-side example below.
  </Step>

  <Step title="Update response handling">
    Classic responses returned a document with `sections[]` (each with `key`, `name`, `text`). The new `POST /documents` response wraps everything in a `document` object — its `templateId`, `templateVersionId`, and `document.stringDocument` (a `{sectionId: renderedString}` map — keyed by **section UUID**, not slug/key) and/or `document.structuredDocument`, plus a top-level `usageInfo.creditsConsumed`. See [Guided Synthesis — response shape](/textgen/documents-guided-synthesis#response-shape).
  </Step>

  <Step title="Decide your interaction story">
    Classic was always interaction-scoped. The new endpoint is interaction-**optional**. If you still want facts/transcripts attached to an interaction to flow in implicitly, pass `interactionId`. If you'd rather pass everything explicitly, supply a `context` array. The two are **mutually exclusive today** — combining them is on the roadmap.
  </Step>
</Steps>

## Worked example — Classic → new, side by side

The Classic snippet below assembles four sections with overrides on three of them. Compare it to the new equivalent.

### Before (Classic)

```ts title="POST /interactions/{id}/documents — Classic" expandable theme={null}
const document = await client.documents.create(interactionId, {
  context: [
    {
      type: "facts",
      data: [
        { text: "32 year-old female", group: "demographics", source: "system" },
        { text: "itchy rash, started last week", group: "history-of-present-illness", source: "core" },
        { text: "allergic to birch pollen since childhood", group: "allergies", source: "core" },
        { text: "typical eczema appearance", group: "assessment", source: "core" },
        { text: "corticosteroid treatment for 2 weeks, taper-down 2 weeks", group: "plan", source: "core" },
        { text: "follow-up in 6 weeks", group: "plan", source: "core" },
      ],
    },
  ],
  template: {
    description: "Clinical dermatology note",
    sections: [
      {
        key: "corti-hpi",
        nameOverride: "Anamnesis",
        additionalInstructionsOverride: "- refer to patient as my case\n- do not include allergies",
      },
      { key: "corti-allergies" },
      {
        key: "corti-assessment",
        formatRuleOverride: "use fixed subheadings: **Diagnosis**:[content]\n\n**Assessment**:[content]:",
        writingStyleOverride: "telegraphic, clinical neutral",
      },
      {
        key: "corti-plan",
        formatRuleOverride: "Single paragraph in running text",
      },
    ],
  },
  outputLanguage: "en",
  name: "test of override customizations",
  documentationMode: "routed_parallel",
});
```

### After (new — Path 3 `assemblyTemplate`)

This Classic call assembled sections inline, so **Path 3** is the right target. (If you'd instead been calling a stored template by `templateKey`, you'd use Path 2.)

`formatRuleOverride` on `corti-assessment` and `corti-plan` does not have a runtime equivalent on its own — for that you'd either supply a full `outputSchema` in the override (wholesale replacement) or create an inheriting section ahead of time with the desired schema. The example below uses inline `outputSchema` overrides to keep the migration in one request.

```ts title="POST /documents — Path 3 (assemblyTemplate)" expandable theme={null}
const result = await client.documents.generate({
  outputLanguage: "en-US",
  context: [
    {
      type: "facts",
      facts: [
        { text: "32 year-old female" },
        { text: "itchy rash, started last week" },
        { text: "allergic to birch pollen since childhood" },
        { text: "typical eczema appearance" },
        { text: "corticosteroid treatment for 2 weeks, taper-down 2 weeks" },
        { text: "follow-up in 6 weeks" },
      ],
    },
  ],
  assemblyTemplate: {
    name: "Clinical dermatology note",
    instructions: {
      prompt: "Clinical dermatology note.",
    },
    sectionRefs: [
      {
        // corti-hpi (en) UUID, resolved once via LIST /documents/sections?lang=en (filter source === "corti" client-side)
        sectionId: "<your-section-id>",
        overrides: {
          heading: "Anamnesis",
          instructions: {
            miscPrompt: "- refer to patient as my case\n- do not include allergies",
          },
        },
      },
      { sectionId: "<your-section-id>" },
      {
        sectionId: "<your-section-id>",
        overrides: {
          instructions: {
            writingStylePrompt: "telegraphic, clinical neutral",
          },
          // formatRuleOverride → typed outputSchema (wholesale replacement).
          // See /textgen/section-schemas for design patterns.
          outputSchema: {
            type: "object",
            fieldFormat: "**Diagnosis**: {diagnosis}\n\n**Assessment**: {assessment}",
            fields: [
              { key: "diagnosis",  description: "Diagnosis", value: { type: "string" } },
              { key: "assessment", description: "Assessment narrative", value: { type: "string" } },
            ],
          },
        },
      },
      {
        sectionId: "<your-section-id>",
        overrides: {
          // formatRuleOverride: "Single paragraph in running text" → tighten the schema
          // and use writingStylePrompt to nudge prose vs. bullets.
          instructions: {
            writingStylePrompt: "Single paragraph in running prose; no bullets, no line breaks.",
          },
          outputSchema: { type: "string" },
        },
      },
    ],
  },
});
```

### Behavior notes on the example

* **Facts context** — pass a `facts` array on `type: "facts"` (Classic used a single `data[]` on one item).
* **`nameOverride` → `heading`**, **`writingStyleOverride` → `writingStylePrompt`**, **`additionalInstructionsOverride` → `miscPrompt`**, **`contentOverride` → `contentPrompt`** — mechanical renames.
* **`formatRuleOverride` rewrites as an `outputSchema`** — wholesale replacement of the section's schema for this call. The example uses `fieldFormat` to lock the fixed subheadings, and a plain `string` schema to force single-paragraph prose.
* The response is `document.stringDocument` (and `document.structuredDocument` for the assessment section because its schema is an object) — not the Classic `sections[]` array. The wrapping shape is `{ document: { ... }, usageInfo: { creditsConsumed } }`.

## Features being aligned

A handful of Classic features don't yet have a public equivalent on `POST /documents`:

* **`documentationMode: routed_parallel`** — the [Documentation Mode](/textgen/documents-docmode) flag.
* **`X-Corti-Retention-Policy: none`** header — opt-out of database persistence per request.
* **`disableGuardrails`** — toggle documentation guardrails per request.

If your Classic integration relies on any of these, reach out to [help@corti.ai](mailto:help@corti.ai) or your Corti contact for the planned equivalent before cutting traffic over. Otherwise, the mapping above covers everyday usage.

## Quick reference

<Columns cols={2}>
  <Card title="Guided Synthesis" href="/textgen/documents-guided-synthesis" icon="wand-sparkles">
    Full reference for `POST /documents` and the four template-supply paths.
  </Card>

  <Card title="Customization Cookbook" href="/textgen/customization-cookbook" icon="book-open">
    Seven recipes from "use Corti Standards as-is" to "your own + customer inheritance + per-call override."
  </Card>

  <Card title="Section Schemas" href="/textgen/section-schemas" icon="braces">
    Schema patterns that replace Classic `formatRuleOverride` and unlock typed outputs.
  </Card>

  <Card title="Corti Standards" href="/textgen/corti-standards" icon="library">
    The library and discovery patterns you'll use to resolve Classic keys to UUIDs.
  </Card>
</Columns>
