> ## 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 - Asynchronous AI Scribe

> Learn how to build an ambient scribe application on the Corti API

Follow these steps to get access and start building:

<Steps titleSize="h3">
  <Step title="Create an account in the Corti Console">
    <AccordionGroup>
      <Accordion title="Get access" icon="key">
        The [Corti Console](https://console.corti.app/) is where you can create an account to access Corti AI

           <Icon icon="check" /> Create an account <br />
           <Icon icon="check" /> Create a project <br />
           <Icon icon="check" /> Create API clients <br />
           <Icon icon="check" /> Use the client ID and client secret in your app <br />
           <Icon icon="check" /> Develop against the API and collaborate with your project team <br />
           <Icon icon="check" /> Monitor usage, manage billing and more
      </Accordion>

      <Accordion title="Walkthrough video" icon="youtube">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/DyeydxVDslQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen />
      </Accordion>
    </AccordionGroup>

    <br />
  </Step>

  <Step title="Authenticate and test the API">
    <AccordionGroup>
      <Accordion title="Authenticate" icon="lock-open">
        Authentication to the API on all environments is governed by OAuth 2.0. This authentication protocol offers enhanced security measures, ensuring that access to patient data and medical documentation is securely managed and compliant with healthcare regulations.

        <Info>
          By default, you will receive a client-id and client-secret to authenticate via `client credentials` grant type.
        </Info>

        <Steps titleSize="h3">
          <Step title="Request an access token">
            To acquire an access token, request a token from Corti Auth using your `client_id` and `client_secret`.

            <CodeGroup>
              ```bash title="cURL (base realm)" theme={null}
              # Replace these with your values
              CLIENT_ID="<your-client-id>"
              CLIENT_SECRET="<your-client-secret>"
              ENVIRONMENT="<eu-or-us>"

              curl \
                "https://auth.${ENVIRONMENT}.corti.app/realms/base/protocol/openid-connect/token" \
                -H 'Content-Type: application/x-www-form-urlencoded' \
                -d "client_id=${CLIENT_ID}" -d "client_secret=${CLIENT_SECRET}" \
                -d 'grant_type=client_credentials' -d 'scope=openid'
              ```

              ```bash title="cURL (custom tenant)" theme={null}
              # Replace these with your values
              CLIENT_ID="<your-client-id>"
              CLIENT_SECRET="<your-client-secret>"
              ENVIRONMENT="<eu-or-us>"
              TENANT="<your-tenant-name>"

              curl \
                "https://auth.${ENVIRONMENT}.corti.app/realms/${TENANT}/protocol/openid-connect/token" \
                -H 'Content-Type: application/x-www-form-urlencoded' \
                -d "client_id=${CLIENT_ID}" -d "client_secret=${CLIENT_SECRET}" \
                -d 'grant_type=client_credentials' -d 'scope=openid'
              ```
            </CodeGroup>

            <Note>Create your account and client credentials in the Corti Console - in addition to client ID and client secret you'll see the tenant name and environment.</Note>
          </Step>

          <Step title="Receive an access token">
            It will return you an `access_token`:

            ```json title="Response" theme={null}
            {
              "access_token": "eyJhbGciOi...",
              "expires_in": 300,
              "token_type": "Bearer",
              "scope": "profile openid email"
            }
            ```

            As you can see, the access token expires after 300 seconds (5 minutes). By default as per oAuth standards, no refresh token is used in this flow. There are many available modules to manage monitoring expiry and acquiring a new access token. However, a refresh token can be enabled if needed.
          </Step>
        </Steps>
      </Accordion>

      <Accordion title="Code examples" icon="code">
        Example **authentication code snippets** for the Corti API in Python, JavaScript, and .NET.

        #### With SDK

        Install SDK to your project:

        <CodeGroup>
          ```bash title="JavaScript" theme={null}
          npm install @corti/sdk
          # or
          yarn add @corti/sdk
          # or
          pnpm add @corti/sdk
          ```

          ```bash title="C# .NET" theme={null}
          dotnet add package Corti.Sdk

          # Alternatively, in your .csproj:
          # <PackageReference Include="Corti.Sdk" Version="*" />
          ```
        </CodeGroup>

        Create a client to call API:

        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          import { CortiClient } from "@corti/sdk";

          // Replace these with your values
          const CLIENT_ID = "<your-client-id>";
          const CLIENT_SECRET = "<your-client-secret>";
          const ENVIRONMENT = "<eu-or-us>";
          const TENANT = "<your-tenant-name>";

          const client = new CortiClient({
              environment: ENVIRONMENT,
              tenantName: TENANT,
              auth: {
                  clientId: CLIENT_ID,
                  clientSecret: CLIENT_SECRET,
              },
          });
          ```

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

          // Replace these with your values
          const string CLIENT_ID = "<your-client-id>";
          const string CLIENT_SECRET = "<your-client-secret>";
          const string ENVIRONMENT = "<eu-or-us>";
          const string TENANT = "<your-tenant-name>";

          var client = new CortiClient(
              tenantName: TENANT,
              environment: ENVIRONMENT,
              auth: CortiClientAuth.ClientCredentials(
                  clientId: CLIENT_ID,
                  clientSecret: CLIENT_SECRET)
          );
          ```
        </CodeGroup>

        If you only need a token:

        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          import { CortiAuth } from "@corti/sdk";

          // Replace these with your values
          const CLIENT_ID = "<your-client-id>";
          const CLIENT_SECRET = "<your-client-secret>";
          const ENVIRONMENT = "<eu-or-us>";
          const TENANT = "<your-tenant-name>";

          const auth = new CortiAuth({
            environment: ENVIRONMENT,
            tenantName: TENANT,
          });

          const token = await auth.getToken({
            clientId: CLIENT_ID,
            clientSecret: CLIENT_SECRET,
          });

          console.log("accessToken:", token.accessToken);
          ```

          {/* Get a token via client credentials using CustomAuthClient */}

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

          // Replace these with your values
          const string CLIENT_ID = "<your-client-id>";
          const string CLIENT_SECRET = "<your-client-secret>";
          const string ENVIRONMENT = "<eu-or-us>";
          const string TENANT = "<your-tenant-name>";

          var auth = CustomAuthClient.Create(
              new CortiAuthClientOptions
              {
                  TenantName = TENANT,
                  Environment = ENVIRONMENT,
              });

          var tokenResponse = await auth.GetTokenAsync(
              new OAuthTokenRequest
              {
                  ClientId = CLIENT_ID,
                  ClientSecret = CLIENT_SECRET,
              });

          Console.WriteLine(tokenResponse.AccessToken);
          ```
        </CodeGroup>

        #### Without SDK

        <CodeGroup>
          ```js title="JavaScript" expandable theme={null}
          // Replace these with your values
          const CLIENT_ID = "<your-client-id>";
          const CLIENT_SECRET = "<your-client-secret>";
          const ENVIRONMENT = "<eu-or-us>";
          const TENANT = "<your-tenant-name>";

          async function getAccessToken() {
            const tokenUrl = `https://auth.${ENVIRONMENT}.corti.app/realms/${TENANT}/protocol/openid-connect/token`;

            const params = new URLSearchParams();
            params.append("client_id", CLIENT_ID);
            params.append("client_secret", CLIENT_SECRET);
            params.append("grant_type", "client_credentials");
            params.append("scope", "openid");

            const res = await fetch(tokenUrl, {
              method: "POST",
              headers: { "Content-Type": "application/x-www-form-urlencoded" },
              body: params
            });

            if (!res.ok) {
              throw new Error(`Failed to get token, status ${res.status}`);
            }

            const data = await res.json();
            return data.access_token;
          }

          // Example usage
          getAccessToken().then(token => {
            console.log("Access token:", token);
          }).catch(err => {
            console.error("Error:", err);
          });
          ```

          ```csharp title="C# .NET" expandable theme={null}
          using System;
          using System.Net.Http;
          using System.Text.Json;
          using System.Threading.Tasks;

          class Program
          {
              private const string CLIENT_ID = "<your-client-id>";
              private const string CLIENT_SECRET = "<your-client-secret>";
              private const string ENVIRONMENT = "<eu-or-us>";
              private const string TENANT = "<your-tenant-name>";

              private static async Task<string> GetAccessTokenAsync()
              {
                  var tokenUrl = $"https://auth.{ENVIRONMENT}.corti.app/realms/{TENANT}/protocol/openid-connect/token";

                  using var http = new HttpClient();

                  var content = new FormUrlEncodedContent(new[]
                  {
                      new KeyValuePair<string, string>("client_id", CLIENT_ID),
                      new KeyValuePair<string, string>("client_secret", CLIENT_SECRET),
                      new KeyValuePair<string, string>("grant_type", "client_credentials"),
                      new KeyValuePair<string, string>("scope", "openid")
                  });

                  var response = await http.PostAsync(tokenUrl, content);
                  response.EnsureSuccessStatusCode();

                  var payload = await response.Content.ReadAsStringAsync();
                  using var json = JsonDocument.Parse(payload);

                  return json.RootElement.GetProperty("access_token").GetString()!;
              }

              static async Task Main()
              {
                  var token = await GetAccessTokenAsync();
                  Console.WriteLine($"Access token: {token}");
              }
          }
          ```

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

          # Replace these with your values
          CLIENT_ID = "<your-client-id>"
          CLIENT_SECRET = "<your-client-secret>"
          ENVIRONMENT = "<eu-or-us>"
          TENANT = "<your-tenant-name>"

          def get_access_token():
              """Request an OAuth2 client-credentials access token from Corti."""
              url = f"https://auth.{ENVIRONMENT}.corti.app/realms/{TENANT}/protocol/openid-connect/token"

              data = {
                  "client_id": CLIENT_ID,
                  "client_secret": CLIENT_SECRET,
                  "grant_type": "client_credentials",
                  "scope": "openid",
              }

              res = requests.post(url, data=data, headers={"Content-Type": "application/x-www-form-urlencoded"})
              res.raise_for_status()
              return res.json()["access_token"]


          # Example usage
          if __name__ == "__main__":
              token = get_access_token()
              print("Access token:", token)
          ```

          ```bash title="cURL" theme={null}
          # Replace these with your values
          CLIENT_ID="<your-client-id>"
          CLIENT_SECRET="<your-client-secret>"
          ENVIRONMENT="<eu-or-us>"
          TENANT="<your-tenant-name>"

          curl "https://auth.${ENVIRONMENT}.corti.app/realms/${TENANT}/protocol/openid-connect/token" \
            -H "Content-Type: application/x-www-form-urlencoded" \
            -d "client_id=${CLIENT_ID}" \
            -d "client_secret=${CLIENT_SECRET}" \
            -d "grant_type=client_credentials" \
            -d "scope=openid"
          ```
        </CodeGroup>
      </Accordion>
    </AccordionGroup>

    <br />

    <AccordionGroup>
      <Accordion title="Make an API request" icon="git-pull-request">
        Once you're authenticated, make requests against the API. Below is a basic example for creating an Interaction. See all API requests and specifications [here](/api-reference/).

        ### With SDK

        REST API:

        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          // Token acquisition and refresh happen automatically
          const interactions = await client.interactions.list();
          ```

          {/* List interactions with async foreach pagination */}

          ```csharp title="C# .NET" theme={null}
          var pager = await client.Interactions.ListAsync(new InteractionsListRequest());
          ```

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

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

          response = requests.get(
              f"https://api.{ENVIRONMENT}.corti.app/v2/interactions",
              headers={
                  "Authorization": f"Bearer {TOKEN}",
                  "Tenant-Name": TENANT,
              },
          )
          response.raise_for_status()
          interactions = response.json()["interactions"]
          ```

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

          curl "https://api.${ENVIRONMENT}.corti.app/v2/interactions" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Tenant-Name: ${TENANT}"
          ```
        </CodeGroup>

        WebSockets:

        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          const streamSocket = await client.stream.connect({
            id: "<your-interaction-id>"
          });
          ```

          {/* Connect to /streams without configuration */}

          ```csharp title="C# .NET" theme={null}
          var stream = await client.CreateStreamApiAsync("<your-interaction-id>");
          await stream.ConnectAsync();
          ```
        </CodeGroup>

        ### Without SDK

        <Steps titleSize="h3">
          <Step title="Call the base URL">
            Subsequently you use the `access_token` to authenticate any API request. The baseURL is dependent on the environment:

            ```bash baseURL API theme={null}
            api.$environment.corti.app/v2
            ```

            <Note>Create your account and client credentials in the Corti Console - in addition to client ID and client secret you'll see the tenant name and environment.</Note>

            <br />

            If, for example, you are on the eu environment and want to create an interaction as the starting point for any other workflow operations your URL will look like this:

            ```bash URL to create an interaction theme={null}
            POST https://api.eu.corti.app/v2/interactions/
            ```
          </Step>

          <Step title="Pass the auth token">
            For **REST API requests**, your `access_token` should be passed as part of the `Request Header`. Additionally you need to include the `Tenant-Name` parameter:

            ```jsx API call request header theme={null}
            Tenant-Name: <tenantname>
            Authorization: Bearer <access_token>
            ```

            <Note>Create your account and client credentials in the Corti Console - in addition to client ID and client secret you'll see the tenant name and environment.</Note>

            <br />

            For **WebSocket** connections, the `access_token` should be passed in as URL parameter. The `Tenant-Name` is already part of the WebSocket url returned with the create interaction request:

            ```javascript wss url with access_token appended theme={null}
            wss://{streams-url}&token=Bearer {access_token}
            ```

            <Tip>Find the full specification for create interaction request in the [API Reference](api-reference/interactions/create-interaction)</Tip>
          </Step>
        </Steps>
      </Accordion>

      <Accordion title="Walkthrough video" icon="youtube">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/NlF39t7uU6A?si=07X7ZH_lEEZJ8Hik" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen />
      </Accordion>
    </AccordionGroup>

    <br />
  </Step>

  <Step title="Building a complete workflow">
    <AccordionGroup>
      <Accordion title="Example workflow design and planning" icon="git-pull-request">
        **Goal:** Integrate with Corti SDK to enable an ambient documentation, AI scribe workflow application

        |     | Decision                                                                                                                                                                   | Integration Planning                                                                                                                                                                                    |
        | :-: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
        |  1  | How will audio be processed - real-time web socket connection, via `/streams` API (**recommended**) or asynchronous processing, via `/recordings` and \`/transcripts APIs? | To use the `/streams` API, follow the path for **real-time ambient documentation**; to use the `/recordings` and `/transcripts` APIs, follow the path of asynchronous ambient documentation.            |
        |  2  | What template(s) will be used in document generation workflow?                                                                                                             | Use the `GET/templates` request to see available templates. In addition to accessing Corti-default templates, you can add the `?org=` filter to find templates available to your specific organization. |
        |  3  | Will document be generated based on `Facts` (**recommended**), `transcript`, or `string`?                                                                                  | Designing around `Facts`, `transcript`, or `string` will determine how to use the `context` variable in the Generate Document request.                                                                  |
        |  4  | Will EHR context or other data be used as `Facts` in document generation?                                                                                                  | If yes, then use the `Add Facts` request to support the context updates.                                                                                                                                |
        |  5  | How will the output be integrated into EHR or other target systems?                                                                                                        | Document output will be a json object that can be saved as a full note, or parsed by section so that text can be directed to appropriate fields in the target system.                                   |
      </Accordion>

      <Accordion title="Asynchronous Ambient Scribing Example" icon="server">
        The first step will be to create an interaction, as shown [above](/sdk/js/overview#create-an-interaction). Once you've created your interaction, the interaction ID can be used for the steps that follow below.

        <Tip>See more details about this workflow [here](/workflows/ambient-async) and the full API specifications [here](/api-reference).</Tip>

        <Steps titleSize="h4">
          <Step title="Upload a recording">
            Here is where you use the `interaction ID`.

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

            Create a helper function to upload a recording:

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

              async function uploadRecording(client, interactionId, filePath){
                  const file = createReadStream(filePath, { autoClose: true });
                  return client.recordings.upload(file, interactionId);
              }
              ```

              ```csharp title="C# .NET" theme={null}
              async Task<RecordingsCreateResponse> UploadRecordingAsync(
                  CortiClient client,
                  string interactionId,
                  string filePath)
              {
                  var file = File.OpenRead(filePath);
                  return await client.Recordings.UploadAsync(interactionId, 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>

            <Note>
              The createReadStream import is used to create a readable stream from a file path.

              Find the full specification for uploading a recording [in the API reference](/api-reference/recordings/upload-recording).
            </Note>
          </Step>

          <Step title="Generate a transcript">
            Here you’ll need the `interactionId` as well as the `recordingId` returned from the upload response.

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

            <Note>Find the full specification for generating a transcript [in the API reference](/api-reference/transcripts/create-transcript)</Note>
          </Step>

          <Step title="Generating a document">
            Here you need the interactionId and transcript text (`transcript`) from the previous step.

            <CodeGroup>
              ```ts title="JavaScript" theme={null}
              const document = await client.documents.create(interactionId, {
                  context: (transcript.transcripts ?? []).map((t) => ({
                      type: "transcript",
                      data: t,
                  })),
                  templateKey: "soap",
                  outputLanguage: "en",
              });
              ```

              ```csharp title="C# .NET" theme={null}
              var document = await client.Documents.CreateAsync(
                  interactionId,
                  DocumentsCreateRequest.FromDocumentsCreateRequestWithTemplateKey(
                      new DocumentsCreateRequestWithTemplateKey
                      {
                          Context = transcript.Transcripts
                              .Select(t =>
                                  DocumentsContext.FromDocumentsContextWithTranscript(
                                      new DocumentsContextWithTranscript
                                      {
                                          Type = DocumentsContextWithTranscriptType.Transcript,
                                          Data = new CommonTranscriptRequest
                                          {
                                              Channel = t.Channel,
                                              Participant = t.Participant,
                                              SpeakerId = t.SpeakerId,
                                              Text = t.Text,
                                              Start = t.Start,
                                              End = t.End,
                                          },
                                      }
                                  )
                              )
                              .ToArray(),
                          TemplateKey = "soap",
                          OutputLanguage = "en",
                      }
                  )
              );
              ```

              ```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>"

              # transcript_segments is a list of dicts with at least a "text" field,
              # typically obtained from the transcripts.create response
              response = requests.post(
                  f"https://api.{ENVIRONMENT}.corti.app/v2/interactions/{INTERACTION_ID}/documents",
                  headers={
                      "Authorization": f"Bearer {TOKEN}",
                      "Tenant-Name": TENANT,
                      "Content-Type": "application/json",
                  },
                  json={
                      "context": [
                          {"type": "transcript", "data": segment}
                          for segment in transcript_segments
                      ],
                      "templateKey": "soap",
                      "outputLanguage": "en",
                  },
              )
              response.raise_for_status()
              document = response.json()
              ```

              ```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 --request POST \
                --url "https://api.${ENVIRONMENT}.corti.app/v2/interactions/${INTERACTION_ID}/documents" \
                --header "Authorization: Bearer ${TOKEN}" \
                --header "Tenant-Name: ${TENANT}" \
                --header "Content-Type: application/json" \
                --data '{
                  "context": [
                    { "type": "transcript", "data": { "text": "...", "channel": 0, "speakerId": "doctor" } }
                  ],
                  "templateKey": "soap",
                  "outputLanguage": "en"
                }'
              ```
            </CodeGroup>

            <Note>Find the full specification for generating a document [in the API reference](/api-reference/documents/generate-document#option-2)</Note>
          </Step>

          <Step title="Retrieving the document">
            If you need to retrieve a generated document again or at a later time (since the `create` call already returns the document), you can do so using the `interactionId` and `documentId` from the previous step.

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

              await client.documents.get(INTERACTION_ID, DOCUMENT_ID);
              ```

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

              var document = await client.Documents.GetAsync(INTERACTION_ID, DOCUMENT_ID);
              ```

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

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

              response = requests.get(
                  f"https://api.{ENVIRONMENT}.corti.app/v2/interactions/{INTERACTION_ID}/documents/{DOCUMENT_ID}",
                  headers={
                      "Authorization": f"Bearer {TOKEN}",
                      "Tenant-Name": TENANT,
                  },
              )
              response.raise_for_status()
              document = response.json()
              ```

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

              curl "https://api.${ENVIRONMENT}.corti.app/v2/interactions/${INTERACTION_ID}/documents/${DOCUMENT_ID}" \
                -H "Authorization: Bearer ${TOKEN}" \
                -H "Tenant-Name: ${TENANT}"
              ```
            </CodeGroup>

            <Note>Find the full specification for retrieving a document in the API reference [here](/api-reference/documents/get-document)</Note>
          </Step>
        </Steps>
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Putting it all together">
    <AccordionGroup>
      <Accordion title="SDK example" icon="square-check">
        <CodeGroup>
          ```ts title="JavaScript" theme={null}
          import { createReadStream } from "fs";
          import { CortiClient } from "@corti/sdk";

          // Replace these with your values
          const CLIENT_ID = "<your-client-id>";
          const CLIENT_SECRET = "<your-client-secret>";
          const ENVIRONMENT = "<eu-or-us>";
          const IDENTIFIER = "<id-of-your-choosing>";
          const TENANT = "<your-tenant-name>";

          const client = new CortiClient({
            environment: ENVIRONMENT,
            tenantName: TENANT,
            auth: {
              clientId: CLIENT_ID,
              clientSecret: CLIENT_SECRET,
            },
          });

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

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

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

          const document = await client.documents.create(interactionId, {
            context: (transcript.transcripts ?? []).map((t) => ({
              type: "transcript",
              data: t,
            })),
            templateKey: "soap",
            outputLanguage: "en",
          });

          console.log(document.name);
          ```

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

          // Replace these with your values
          const string CLIENT_ID = "<your-client-id>";
          const string CLIENT_SECRET = "<your-client-secret>";
          const string ENVIRONMENT = "<eu-or-us>";
          const string IDENTIFIER = "<id-of-your-choosing>";
          const string TENANT = "<your-tenant-name>";

          var client = new CortiClient(
              tenantName: TENANT,
              environment: ENVIRONMENT,
              auth: CortiClientAuth.ClientCredentials(clientId: CLIENT_ID, clientSecret: CLIENT_SECRET)
          );

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

          await using var audioStream = File.OpenRead("sample.mp3");
          var recording = await client.Recordings.UploadAsync(interaction.InteractionId, audioStream);

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

          var document = await client.Documents.CreateAsync(
              interaction.InteractionId,
              DocumentsCreateRequest.FromDocumentsCreateRequestWithTemplateKey(
                  new DocumentsCreateRequestWithTemplateKey
                  {
                      Context = transcript.Transcripts
                          .Select(t =>
                              DocumentsContext.FromDocumentsContextWithTranscript(
                                  new DocumentsContextWithTranscript
                                  {
                                      Type = DocumentsContextWithTranscriptType.Transcript,
                                      Data = new CommonTranscriptRequest
                                      {
                                          Channel = t.Channel,
                                          Participant = t.Participant,
                                          SpeakerId = t.SpeakerId,
                                          Text = t.Text,
                                          Start = t.Start,
                                          End = t.End,
                                      },
                                  }
                              )
                          )
                          .ToArray(),
                      TemplateKey = "soap",
                      OutputLanguage = "en",
                  }
              )
          );

          Console.WriteLine(document.Name);
          ```

          ```python title="Python" expandable theme={null}
          import uuid
          import requests
          from datetime import datetime, timezone

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

          BASE_URL = f"https://api.{ENVIRONMENT}.corti.app/v2"

          HEADERS = {
              "Authorization": f"Bearer {TOKEN}",
              "Tenant-Name":   TENANT,
              "Content-Type":  "application/json",
          }

          # Create interaction
          res = requests.post(f"{BASE_URL}/interactions", headers=HEADERS, json={
              "encounter": {
                  "identifier": str(uuid.uuid4()),
                  "status": "planned",
                  "type": "first_consultation",
              },
          })
          res.raise_for_status()
          interaction_id = res.json()["interactionId"]

          # Upload recording
          with open("sample.mp3", "rb") as f:
              res = requests.post(
                  f"{BASE_URL}/interactions/{interaction_id}/recordings/",
                  headers={**HEADERS, "Content-Type": "application/octet-stream"},
                  data=f,
              )
          res.raise_for_status()
          recording_id = res.json()["recordingId"]

          # Create transcript
          res = requests.post(
              f"{BASE_URL}/interactions/{interaction_id}/transcripts/",
              headers=HEADERS,
              json={"recordingId": recording_id, "primaryLanguage": "en"},
          )
          res.raise_for_status()
          transcript = res.json()

          # Create document from transcript
          segments = transcript.get("transcripts") or []
          res = requests.post(
              f"{BASE_URL}/interactions/{interaction_id}/documents",
              headers=HEADERS,
              json={
                  "context": [{"type": "transcript", "data": t} for t in segments],
                  "templateKey": "soap",
                  "outputLanguage": "en",
              },
          )
          res.raise_for_status()
          document = res.json()
          print(document["name"])
          ```
        </CodeGroup>
      </Accordion>
    </AccordionGroup>
  </Step>
</Steps>
