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

# Quickstart — Real-time & Asynchronous

> Get a working end-to-end Corti Text Generation flow — from clinical audio (or a stored recording) to a guided, structured clinical note — using the Corti SDK.

<Tip>Completing this quickstart will get you an end-to-end text generation flow — from audio (or an uploaded recording) all the way to a **guided-synthesized clinical note**. Both scenarios build on Corti's [official SDKs](/sdk/overview) and end with the same [Guided Documents](/textgen/documents-guided-synthesis) call.</Tip>

## Prerequisites

* **A Corti Console account with an API client** — if you haven't set one up yet, follow [Creating API Clients](/authentication/creating_clients) and the [Authentication Quickstart](/authentication/quickstart). You'll need your `clientId`, `clientSecret`, `tenantName`, and `environment` (`eu` or `us`).
* **The Corti SDK installed and a client created** — install (`@corti/sdk` or `Corti.Sdk`) and initialize a client by following the [Authentication Quickstart](/authentication/quickstart) or the [SDKs overview](/sdk/overview). The SDK handles OAuth 2.0 token acquisition and refresh from your credentials automatically — no manual bearer-token juggling.
* **A `templateId`** — the UUID of a stored template to synthesize the note against. Discover Corti Standards or your own via [`LIST /documents/templates?lang=<locale>`](/api-reference/guided-templates/list-templates), or [browse Corti Standards](/textgen/corti-standards).

## Pick your scenario

Both scenarios end with the same **guided document generation** call. They differ only in how audio is captured and turned into a transcript.

<Tabs>
  <Tab title="Real-time ambient scribe">
    <Steps titleSize="h3">
      <Step title="Create an interaction">
        An interaction anchors the session — the transcript and facts your stream produces are persisted under it, and the doc-gen step reads them back.

        <CodeGroup>
          {/* Create an interaction with the minimal required fields */}

          ```ts title="JavaScript" theme={null}
          // Replace with an identifier of your choosing
          const IDENTIFIER = "<id-of-your-choosing>";

          const { interactionId } = await client.interactions.create({
            encounter: {
              identifier: IDENTIFIER,
              status: "planned",
              type: "first_consultation",
            },
          });
          ```

          {/* Create an interaction with the minimal required fields */}

          ```csharp title="C# .NET" theme={null}
          // Replace with an identifier of your choosing
          const string IDENTIFIER = "<id-of-your-choosing>";

          var interaction = await client.Interactions.CreateAsync(new InteractionsCreateRequest
          {
              Encounter = new InteractionsEncounterCreateRequest
              {
                  Identifier = IDENTIFIER,
                  Status = InteractionsEncounterStatusEnum.Planned,
                  Type = InteractionsEncounterTypeEnum.FirstConsultation,
              },
          });
          ```

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

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

          response = requests.post(
              f"https://api.{ENVIRONMENT}.corti.app/v2/interactions",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
                  "Content-Type": "application/json",
              },
              json={
                  "encounter": {
                      "identifier": IDENTIFIER,
                      "status": "planned",
                      "type": "first_consultation",
                  },
              },
          )
          response.raise_for_status()
          interaction = response.json()
          interaction_id = interaction["interactionId"]
          ```

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

          curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/interactions" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Tenant-Name: ${TENANT}" \
            -H "Content-Type: application/json" \
            -d '{
              "encounter": {
                "identifier": "'"${IDENTIFIER}"'",
                "status": "planned",
                "type": "first_consultation"
              }
            }'
          ```
        </CodeGroup>

        <Info>Only `encounter` (with `identifier`, `status`, and `type`) is required. `assignedUserId`, `patient`, `period`, and `title` are optional — see [Create Interaction](/api-reference/interactions/create-interaction) for the full schema.</Info>
      </Step>

      <Step title="Capture and stream audio in real time">
        For live capture in the browser, the [Ambient Web Component](/sdk/ambient/overview) (`@corti/ambient-web`) is the fastest path — the `<corti-ambient>` element handles microphone permissions, device selection, audio streaming, and transcript/facts delivery for you. Point it at the `interactionId` from step 1 and configure it for **facts** (real-time extraction).

        ```html theme={null}
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>Corti Ambient</title>
            <script type="module" src="https://cdn.jsdelivr.net/npm/@corti/ambient-web/dist/bundle.js"></script>
        </head>
        <body>
            <corti-ambient id="ambient" interactionId="<your-interaction-id>"></corti-ambient>

            <script>
                const ambient = document.getElementById("ambient");

                ambient.addEventListener("ready", () => {
                    ambient.accessToken = "<your-access-token>";
                    ambient.ambientConfig = {
                        transcription: { primaryLanguage: "en" },
                        mode: { type: "facts", outputLocale: "en" },
                    };
                });

                ambient.addEventListener("transcript", (event) => {
                    // Segments can arrive out of order across speakers — order by time.start
                    [...event.detail.data]
                        .sort((a, b) => a.time.start - b.time.start)
                        .forEach((seg) => {
                            console.log(`[${seg.time.start}s → ${seg.time.end}s]: ${seg.transcript}`);
                        });
                });

                ambient.addEventListener("facts", (event) => {
                    event.detail.fact.forEach((fact) => {
                        console.log(`Fact [${fact.group}]: ${fact.text}`);
                    });
                });
            </script>
        </body>
        </html>
        ```

        <Note>The web component does **not** manage OAuth — supply an `accessToken` (or a token-refresh function); see the [Ambient authentication guide](/sdk/ambient/authentication). In production you'd create the interaction server-side and pass the `interactionId` and token to the browser.</Note>

        <Note>The component persists transcripts and (with `mode.type: "facts"`) extracted facts under the interaction — that's the data the doc-gen step reasons over. Before generating the note, wait until recording has stopped **and** audio processing has finished (`recording-state-changed` reports `detail.processing: false`, or call `closeConnection()`, which sends `end` and waits for the server's `ended` response) so every transcript segment and fact is stored.</Note>

        <Tip>Streaming audio from your own pipeline — server-side, or a non-browser client? Skip the web component and use the lower-level `client.stream.connect()` + `sendAudio()` methods from the [JavaScript](/sdk/js/websockets) or [.NET](/sdk/dotnet/websockets) SDK. See the [SDKs overview](/sdk/overview) for the full menu.</Tip>
      </Step>

      <Step title="Generate a note from the interaction">
        Once the session has ended, generate the note by referencing the `interactionId` — the SDK pulls the persisted facts and transcript implicitly. `templateRef` picks the template you want.

        <CodeGroup>
          ```ts title="JavaScript" expandable theme={null}
          // Replace these with your values
          const INTERACTION_ID = "<your-interaction-id>";
          const TEMPLATE_ID = "<your-template-id>";

          const result = await client.documents.generate({
            outputLanguage: "en-US",
            interactionId: INTERACTION_ID,
            templateRef: { templateId: TEMPLATE_ID },
          });
          ```

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

          // Replace these with your values
          const string InteractionId = "<your-interaction-id>";
          const string TemplateId = "<your-template-id>";

          var result = await client.Documents.GenerateAsync(
              new GuidedDocumentsGenerateByTemplateRef
              {
                  OutputLanguage = "en-US",
                  InteractionId = InteractionId,
                  TemplateRef = new GuidedTemplateRef { TemplateId = TemplateId },
              });
          ```

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

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

          response = requests.post(
              f"https://api.{ENVIRONMENT}.corti.app/v2/documents",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
                  "Content-Type": "application/json",
              },
              json={
                  "outputLanguage": "en-US",
                  "interactionId": INTERACTION_ID,
                  "templateRef": {"templateId": TEMPLATE_ID},
              },
          )
          response.raise_for_status()
          result = response.json()
          ```

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

          curl --request POST \
            --url "https://api.${ENVIRONMENT}.corti.app/v2/documents" \
            --header "Authorization: Bearer ${TOKEN}" \
            --header "Tenant-Name: ${TENANT}" \
            --header "Content-Type: application/json" \
            --data "{
              \"outputLanguage\": \"en-US\",
              \"interactionId\": \"${INTERACTION_ID}\",
              \"templateRef\": { \"templateId\": \"${TEMPLATE_ID}\" }
            }"
          ```
        </CodeGroup>

        <Tip>This is [Path 1 — plain `templateRef`](/textgen/documents-guided-synthesis/path-1-templateref) of Guided Document Synthesis. For per-user tweaks (Path 2), runtime section assembly (Path 3), or inline definitions (Path 4), see [the four paths](/textgen/documents-guided-synthesis#the-four-template-supply-paths).</Tip>

        <Info>Full specification: [Generate a structured document](/api-reference/guided-documents/generate-a-structured-document).</Info>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Asynchronous">
    <Steps titleSize="h3">
      <Step title="Create an interaction">
        <CodeGroup>
          {/* Create an interaction with the minimal required fields */}

          ```ts title="JavaScript" theme={null}
          // Replace with an identifier of your choosing
          const IDENTIFIER = "<id-of-your-choosing>";

          const { interactionId } = await client.interactions.create({
            encounter: {
              identifier: IDENTIFIER,
              status: "planned",
              type: "first_consultation",
            },
          });
          ```

          {/* Create an interaction with the minimal required fields */}

          ```csharp title="C# .NET" theme={null}
          // Replace with an identifier of your choosing
          const string IDENTIFIER = "<id-of-your-choosing>";

          var interaction = await client.Interactions.CreateAsync(new InteractionsCreateRequest
          {
              Encounter = new InteractionsEncounterCreateRequest
              {
                  Identifier = IDENTIFIER,
                  Status = InteractionsEncounterStatusEnum.Planned,
                  Type = InteractionsEncounterTypeEnum.FirstConsultation,
              },
          });
          ```

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

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

          response = requests.post(
              f"https://api.{ENVIRONMENT}.corti.app/v2/interactions",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
                  "Content-Type": "application/json",
              },
              json={
                  "encounter": {
                      "identifier": IDENTIFIER,
                      "status": "planned",
                      "type": "first_consultation",
                  },
              },
          )
          response.raise_for_status()
          interaction = response.json()
          interaction_id = interaction["interactionId"]
          ```

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

          curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/interactions" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Tenant-Name: ${TENANT}" \
            -H "Content-Type: application/json" \
            -d '{
              "encounter": {
                "identifier": "'"${IDENTIFIER}"'",
                "status": "planned",
                "type": "first_consultation"
              }
            }'
          ```
        </CodeGroup>

        <Info>Only `encounter` (with `identifier`, `status`, and `type`) is required. `assignedUserId`, `patient`, `period`, and `title` are optional — see [Create Interaction](/api-reference/interactions/create-interaction) for the full schema.</Info>
      </Step>

      <Step title="Upload the recording">
        Attach the audio file to the interaction.

        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          import { createReadStream } from "fs";

          // Replace these with your values
          const INTERACTION_ID = "<your-interaction-id>";

          const file = createReadStream("sample.mp3", { autoClose: true });
          await client.recordings.upload(file, INTERACTION_ID);
          ```

          ```csharp title="C# .NET" theme={null}
          // Replace these with your values
          const string INTERACTION_ID = "<your-interaction-id>";

          var file = File.OpenRead("sample.mp3");
          var recording = await client.Recordings.UploadAsync(INTERACTION_ID, file);
          ```

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

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

          with open("sample.mp3", "rb") as f:
              response = requests.post(
                  f"https://api.{ENVIRONMENT}.corti.app/v2/interactions/{INTERACTION_ID}/recordings/",
                  headers={
                      "Authorization": f"Bearer {TOKEN}",
                      "Tenant-Name": TENANT,
                      "Content-Type": "application/octet-stream",
                  },
                  data=f,
              )
          response.raise_for_status()
          recording_id = response.json()["recordingId"]
          ```

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

          curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/interactions/${INTERACTION_ID}/recordings/" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Tenant-Name: ${TENANT}" \
            -H "Content-Type: application/octet-stream" \
            --data-binary "@sample.mp3"
          ```
        </CodeGroup>

        <Info>Full specification: [Upload Recording](/api-reference/recordings/upload-recording).</Info>
      </Step>

      <Step title="Generate a transcript">
        Kick off transcription for the uploaded recording. You'll need the `interactionId` from step 1 and the `recordingId` from step 2.

        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          const { recordingId } = await uploadRecording(client, interactionId, "sample.mp3");

          const transcript = await client.transcripts.create(interactionId, {
              recordingId: recordingId,
              primaryLanguage: "en"
          });
          ```

          ```csharp title="C# .NET" theme={null}
          var recording = await UploadRecordingAsync(client, interactionId, "sample.mp3");

          var transcript = await client.Transcripts.CreateAsync(
              interactionId,
              new TranscriptsCreateRequest
              {
                  RecordingId = recording.RecordingId,
                  PrimaryLanguage = "en",
              }
          );
          ```

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

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

          response = requests.post(
              f"https://api.{ENVIRONMENT}.corti.app/v2/interactions/{INTERACTION_ID}/transcripts/",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
                  "Content-Type": "application/json",
              },
              json={
                  "recordingId": RECORDING_ID,
                  "primaryLanguage": "en",
              },
          )
          response.raise_for_status()
          transcript = response.json()
          ```

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

          curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/interactions/${INTERACTION_ID}/transcripts/" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Tenant-Name: ${TENANT}" \
            -H "Content-Type: application/json" \
            -d '{
              "recordingId": "'"${RECORDING_ID}"'",
              "primaryLanguage": "en"
            }'
          ```
        </CodeGroup>

        <Info>Full specification: [Create Transcript](/api-reference/transcripts/create-transcript).</Info>
      </Step>

      <Step title="Extract and store facts (optional)">
        Guided doc-gen already reasons over the transcript. To also ground it on structured **facts** — the same signal the real-time path captures automatically — extract them from the transcript text with `tools/extract-facts`.

        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          // Replace with your transcript text (e.g. the transcript segments from the previous step, joined)
          const TRANSCRIPT_TEXT = "<your-transcript-text>";

          const { facts } = await client.facts.extract({
            context: [{ type: "text", text: TRANSCRIPT_TEXT }],
            outputLanguage: "en",
          });
          ```

          ```csharp title="C# .NET" theme={null}
          // Replace with your transcript text (e.g. the transcript segments from the previous step, joined)
          const string TRANSCRIPT_TEXT = "<your-transcript-text>";

          var factsResponse = await client.Facts.ExtractAsync(new FactsExtractRequest
          {
              Context = new[]
              {
                  new CommonTextContext
                  {
                      Type = new CommonTextContext.TypeLiteral(),
                      Text = TRANSCRIPT_TEXT,
                  },
              },
              OutputLanguage = "en",
          });
          ```

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

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

          response = requests.post(
              f"https://api.{ENVIRONMENT}.corti.app/v2/tools/extract-facts",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
                  "Content-Type": "application/json",
              },
              json={
                  "context": [{"type": "text", "text": TRANSCRIPT_TEXT}],
                  "outputLanguage": "en",
              },
          )
          response.raise_for_status()
          facts = response.json()["facts"]
          ```

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

          curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/tools/extract-facts" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Tenant-Name: ${TENANT}" \
            -H "Content-Type: application/json" \
            -d '{
              "context": [{ "type": "text", "text": "<your-transcript-text>" }],
              "outputLanguage": "en"
            }'
          ```
        </CodeGroup>

        <Warning>`tools/extract-facts` is stateless — it returns facts but **does not persist them**. To make them available to guided document generation (which reads facts stored under the interaction), write them back with `POST /interactions/{id}/facts`.</Warning>

        <CodeGroup>
          {/* Add facts to an interaction */}

          ```ts title="JavaScript" theme={null}
          // Replace these with your values
          const INTERACTION_ID = "<your-interaction-id>";

          await client.facts.create(INTERACTION_ID, {
            facts: [
              {
                text: "Patient has a history of hypertension.",
                group: "other",
              },
            ],
          });
          ```

          {/* Add facts to an interaction */}

          ```csharp title="C# .NET" theme={null}
          // Replace these with your values
          const string INTERACTION_ID = "<your-interaction-id>";

          await client.Facts.CreateAsync(
              INTERACTION_ID,
              new FactsCreateRequest
              {
                  Facts = new List<FactsCreateInput>
                  {
                      new() { Text = "Patient has a history of hypertension.", Group = "other" },
                  },
              }
          );
          ```

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

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

          response = requests.post(
              f"https://api.{ENVIRONMENT}.corti.app/v2/interactions/{INTERACTION_ID}/facts",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
                  "Content-Type": "application/json",
              },
              json={
                  "facts": [
                      {
                          "text": "Patient has a history of hypertension.",
                          "group": "other",
                      }
                  ]
              },
          )
          response.raise_for_status()
          ```

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

          curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/interactions/${INTERACTION_ID}/facts" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Tenant-Name: ${TENANT}" \
            -H "Content-Type: application/json" \
            -d '{
              "facts": [
                { "text": "Patient has a history of hypertension.", "group": "other" }
              ]
            }'
          ```
        </CodeGroup>

        <Info>Full specifications: [Extract Facts](/api-reference/facts/extract-facts) and [Add Facts](/api-reference/facts/add-facts).</Info>
      </Step>

      <Step title="Generate a note from the interaction">
        Same as the real-time path — reference the `interactionId` and a `templateId`. The SDK pulls the persisted transcript (and any facts) implicitly.

        <CodeGroup>
          ```ts title="JavaScript" expandable theme={null}
          // Replace these with your values
          const INTERACTION_ID = "<your-interaction-id>";
          const TEMPLATE_ID = "<your-template-id>";

          const result = await client.documents.generate({
            outputLanguage: "en-US",
            interactionId: INTERACTION_ID,
            templateRef: { templateId: TEMPLATE_ID },
          });
          ```

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

          // Replace these with your values
          const string InteractionId = "<your-interaction-id>";
          const string TemplateId = "<your-template-id>";

          var result = await client.Documents.GenerateAsync(
              new GuidedDocumentsGenerateByTemplateRef
              {
                  OutputLanguage = "en-US",
                  InteractionId = InteractionId,
                  TemplateRef = new GuidedTemplateRef { TemplateId = TemplateId },
              });
          ```

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

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

          response = requests.post(
              f"https://api.{ENVIRONMENT}.corti.app/v2/documents",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
                  "Content-Type": "application/json",
              },
              json={
                  "outputLanguage": "en-US",
                  "interactionId": INTERACTION_ID,
                  "templateRef": {"templateId": TEMPLATE_ID},
              },
          )
          response.raise_for_status()
          result = response.json()
          ```

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

          curl --request POST \
            --url "https://api.${ENVIRONMENT}.corti.app/v2/documents" \
            --header "Authorization: Bearer ${TOKEN}" \
            --header "Tenant-Name: ${TENANT}" \
            --header "Content-Type: application/json" \
            --data "{
              \"outputLanguage\": \"en-US\",
              \"interactionId\": \"${INTERACTION_ID}\",
              \"templateRef\": { \"templateId\": \"${TEMPLATE_ID}\" }
            }"
          ```
        </CodeGroup>

        <Info>Full specification: [Generate a structured document](/api-reference/guided-documents/generate-a-structured-document).</Info>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## What you get back

Guided document generation returns a `document` plus `usageInfo`:

```json theme={null}
{
  "document": {
    "id": "<your-document-id>",
    "templateId": "<your-template-id>",
    "templateVersionId": "<your-version-id>",
    "language": "en-US",
    "stringDocument": {
      "<your-section-id>": "History of present illness paragraph...",
      "<your-section-id>": "- Item 1\n- Item 2\n..."
    },
    "structuredDocument": null,
    "createdAt": "2026-05-20T10:00:00Z",
    "updatedAt": "2026-05-20T10:00:00Z"
  },
  "usageInfo": {
    "creditsConsumed": 0.029016
  }
}
```

* **`stringDocument`** — the rendered per-section output, keyed by section UUID. Always present.
* **`structuredDocument`** — populated when one or more sections declare an `outputSchema`. Keyed the same way; each entry holds the schema-typed value (string, number, boolean, object, or array). A string leaf is a valid `outputSchema`, so string-typed sections show up here too — this field reflects *whether* a schema was declared, not whether the type is non-string.
* **`templateVersionId`** — the exact template version used. Store it for reproducibility.

See the full response shape in the [Guided Documents overview](/textgen/documents-guided-synthesis#response-shape).

## Next steps

<Columns cols={3}>
  <Card title="Beginner's Guide" href="/textgen/beginners-guide" icon="graduation-cap">
    How facts, sections, templates, and schemas fit together — in plain language.
  </Card>

  <Card title="Guided Document Synthesis" href="/textgen/documents-guided-synthesis" icon="wand-sparkles">
    The endpoint reference. Four template-supply paths, input context options, response shape.
  </Card>

  <Card title="Sections & Templates" href="/textgen/sections-templates-overview" icon="puzzle">
    Author your own sections and templates or inherit from Corti Standards.
  </Card>

  <Card title="Corti Standards library" href="/textgen/corti-standards" icon="library">
    Browse the curated catalogue to find a `templateId` to use in the doc-gen step.
  </Card>

  <Card title="Prompting Best-practices" href="/textgen/prompting-cookbook" icon="notebook-pen">
    Patterns for `contentPrompt`, `writingStylePrompt`, `miscPrompt`, and `outputSchema` descriptions.
  </Card>

  <Card title="Customization Guide" href="/textgen/customization-cookbook" icon="book-open">
    Inherit, override at runtime, or author your own — with per-customer and per-user patterns.
  </Card>
</Columns>

<Note>Please [contact us](https://help.corti.app) if you run into issues with your first end-to-end request.</Note>
