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

# Generate SOAP Notes using LLM Gateway

The acronym SOAP stands for Subjective, Objective, Assessment, and Plan. This standardized method of documenting patient encounters allows providers to concisely record patient information. This guide walks through how to generate SOAP notes with AssemblyAI's LLM Gateway, which provides access to multiple LLM providers through a unified API.

## Get Started

Before we begin, make sure you have an AssemblyAI account and an API key. You can [sign up for an AssemblyAI account](https://www.assemblyai.com/app) and get your API key from your dashboard. You will need to upgrade your account by adding a credit card to have access to LLM Gateway.

Find more details on the current pricing in the AssemblyAI [pricing page](https://www.assemblyai.com/pricing).

## Step-by-Step Instructions

Install the required packages:

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```bash theme={null}
    pip install requests
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```bash theme={null}
    npm install fs-extra
    ```
  </Tab>
</Tabs>

Set up your transcription with PII redaction enabled to protect patient information:

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    import requests
    import time

    base_url = "https://api.assemblyai.com"

    headers = {
        "authorization": "<YOUR_API_KEY>"
    }

    with open("./local_file.mp3", "rb") as f:
        response = requests.post(base_url + "/v2/upload",
                                headers=headers,
                                data=f)

    upload_url = response.json()["upload_url"]

    data = {
        "audio_url": upload_url, # You can also use a URL to an audio or video file on the web
        "speech_models": ["universal-3-pro"],
        "redact_pii": True,
        "redact_pii_policies": ["person_name", "organization", "occupation"],
        "redact_pii_sub": "hash"
    }

    url = base_url + "/v2/transcript"
    response = requests.post(url, json=data, headers=headers)

    transcript_id = response.json()['id']
    polling_endpoint = base_url + "/v2/transcript/" + transcript_id

    print(f"Transcript ID: {transcript_id}")

    while True:
        transcription_result = requests.get(polling_endpoint, headers=headers).json()

        if transcription_result['status'] == 'completed':
            print(transcription_result['text'])
            break
        elif transcription_result['status'] == 'error':
            raise RuntimeError(f"Transcription failed: {transcription_result['error']}")
        else:
            time.sleep(3)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    import fs from "fs-extra";

    const baseUrl = "https://api.assemblyai.com";

    const headers = {
      authorization: "<YOUR_API_KEY>",
    };

    const audioData = await fs.readFile("./local_file.mp3");
    let res = await fetch(`${baseUrl}/v2/upload`, {
      method: "POST",
      headers,
      body: audioData,
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const uploadResponse = await res.json();
    const uploadUrl = uploadResponse.upload_url;

    const data = {
      audio_url: uploadUrl, // You can also use a URL to an audio or video file on the web
      speech_models: ["universal-3-pro"],
      redact_pii: true,
      redact_pii_policies: ["person_name", "organization", "occupation"],
      redact_pii_sub: "hash",
    };

    const url = `${baseUrl}/v2/transcript`;
    res = await fetch(url, {
      method: "POST",
      headers: { ...headers, "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const transcriptResponse = await res.json();

    const transcriptId = transcriptResponse.id;
    const pollingEndpoint = `${baseUrl}/v2/transcript/${transcriptId}`;

    console.log("Transcript ID:", transcriptId);

    let transcriptionResult;
    while (true) {
      res = await fetch(pollingEndpoint, { headers });
      if (!res.ok) throw new Error(`Error: ${res.status}`);
      transcriptionResult = await res.json();

      if (transcriptionResult.status === "completed") {
        console.log(transcriptionResult.text);
        break;
      } else if (transcriptionResult.status === "error") {
        throw new Error(`Transcription failed: ${transcriptionResult.error}`);
      } else {
        await new Promise((resolve) => setTimeout(resolve, 3000));
      }
    }
    ```
  </Tab>
</Tabs>

## Two different methods of generating SOAP notes

### Method 1: Complete SOAP Note Generation

This method uses LLM Gateway to generate the entire SOAP note in one request. The prompt includes context about the audio file, a specific format for the SOAP note, and detailed instructions for each section.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    prompt = """
    Generate a SOAP note summary based on the following doctor-patient appointment transcript.

    Context: This is a doctor appointment between patient and provider. The personal identification information has been redacted.

    Format the response as follows:

    Subjective
    This is typically the shortest section (only 2-3 sentences) and it describes the patient's affect, as the professional sees it. This information is all subjective (it isn't measureable).
    - Include information that may have affected the patient's performance, such as if they were sick, tired, attentive, distractible, etc.
    - Was the patient on time or did they come late?
    - May include a quote of something the patient said, or how they reported feeling

    Objective
    This section includes factual, measurable, and objective information. This may include:
    - Direct patient quotes
    - Measurements
    - Data on patient performance

    Assessment
    This section should be the meat of the SOAP note. It contains a narrative of what actually happened during the session. There may be information regarding:
    - Whether improvements have been made since the last session
    - Any potential barriers to success
    - Clinician's interpretation of the results of the session

    Plan
    This is another short section that states the plan for future sessions. In most settings, this section may be bulleted
    """

    # Send to LLM Gateway
    llm_gateway_data = {
        "model": "claude-sonnet-4-5-20250929",
        "messages": [
            {"role": "user", "content": f"{prompt}\n\n{{{{ transcript }}}}"}
        ],
        "transcript_id": transcript_id,
        "max_tokens": 2000
    }

    response = requests.post(
        "https://llm-gateway.assemblyai.com/v1/chat/completions",
        headers=headers,
        json=llm_gateway_data
    )

    result = response.json()["choices"][0]["message"]["content"]
    print(result.strip())
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    const prompt = `
    Generate a SOAP note summary based on the following doctor-patient appointment transcript.

    Context: This is a doctor appointment between patient and provider. The personal identification information has been redacted.

    Format the response as follows:

    Subjective
    This is typically the shortest section (only 2-3 sentences) and it describes the patient's affect, as the professional sees it. This information is all subjective (it isn't measureable).
    - Include information that may have affected the patient's performance, such as if they were sick, tired, attentive, distractible, etc.
    - Was the patient on time or did they come late?
    - May include a quote of something the patient said, or how they reported feeling

    Objective
    This section includes factual, measurable, and objective information. This may include:
    - Direct patient quotes
    - Measurements
    - Data on patient performance

    Assessment
    This section should be the meat of the SOAP note. It contains a narrative of what actually happened during the session. There may be information regarding:
    - Whether improvements have been made since the last session
    - Any potential barriers to success
    - Clinician's interpretation of the results of the session

    Plan
    This is another short section that states the plan for future sessions. In most settings, this section may be bulleted
    `;

    // Send to LLM Gateway
    const llmGatewayData = {
      model: "claude-sonnet-4-5-20250929",
      messages: [
        { role: "user", content: `${prompt}\n\n{{ transcript }}` },
      ],
      transcript_id: transcriptId,
      max_tokens: 2000,
    };

    let res = await fetch("https://llm-gateway.assemblyai.com/v1/chat/completions", {
      method: "POST",
      headers: { ...headers, "Content-Type": "application/json" },
      body: JSON.stringify(llmGatewayData),
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const response = await res.json();

    const result = response.choices[0].message.content;
    console.log(result.trim());
    ```
  </Tab>
</Tabs>

### Method 2: Section-by-Section Generation

This method generates each section of the SOAP note separately using LLM Gateway. This makes it easy to regenerate one or more sections individually if needed. Each section is generated with a specific prompt tailored to that part of the SOAP note.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    # Define questions for each SOAP section
    questions = [
        {
            "section": "Subjective",
            "question": "What are the patient's current symptoms or concerns?",
            "context": "Gather information about the patient's subjective experience. Example: The patient reports experiencing persistent headaches and dizziness.",
            "format": "<patient's symptoms or concerns>, [exact quote from patient]"
        },
        {
            "section": "Objective",
            "question": "What are the measurable and observable findings from the examination?",
            "context": "Collect data on the patient's objective signs and measurements. Example: The examination reveals an elevated body temperature and increased heart rate.",
            "format": "<measurable and observable findings>"
        },
        {
            "section": "Assessment",
            "question": "Based on the patient's history and examination, what is your assessment or diagnosis?",
            "context": "Formulate a professional assessment based on the gathered information. Example: Based on the patient's symptoms, examination, and medical history, the preliminary diagnosis is migraine.",
            "format": "<assessment or diagnosis>"
        },
        {
            "section": "Plan",
            "question": "What is the plan of action or treatment for the patient?",
            "context": "Outline the intended course of action or treatment. Example: The treatment plan includes prescribing medication, recommending rest, and scheduling a follow-up appointment in two weeks.",
            "format": "<plan of action or treatment>, [exact quote from provider]"
        }
    ]

    # Process each section
    for q in questions:
        prompt = f"""
    {q['question']}

    Context: This is a doctor appointment between patient and provider. The personal identification information has been redacted. {q['context']}

    Answer Format: {q['format']}
    """

        llm_gateway_data = {
            "model": "claude-sonnet-4-5-20250929",
            "messages": [
                {"role": "user", "content": f"{prompt}\n\n{{{{ transcript }}}}"}
            ],
            "transcript_id": transcript_id,
            "max_tokens": 1000
        }

        response = requests.post(
            "https://llm-gateway.assemblyai.com/v1/chat/completions",
            headers=headers,
            json=llm_gateway_data
        )

        result = response.json()["choices"][0]["message"]["content"]
        print(f"{q['section']}: {q['question']}")
        print(result.strip())
        print()
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    // Define questions for each SOAP section
    const questions = [
      {
        section: "Subjective",
        question: "What are the patient's current symptoms or concerns?",
        context: "Gather information about the patient's subjective experience. Example: The patient reports experiencing persistent headaches and dizziness.",
        format: "<patient's symptoms or concerns>, [exact quote from patient]",
      },
      {
        section: "Objective",
        question: "What are the measurable and observable findings from the examination?",
        context: "Collect data on the patient's objective signs and measurements. Example: The examination reveals an elevated body temperature and increased heart rate.",
        format: "<measurable and observable findings>",
      },
      {
        section: "Assessment",
        question: "Based on the patient's history and examination, what is your assessment or diagnosis?",
        context: "Formulate a professional assessment based on the gathered information. Example: Based on the patient's symptoms, examination, and medical history, the preliminary diagnosis is migraine.",
        format: "<assessment or diagnosis>",
      },
      {
        section: "Plan",
        question: "What is the plan of action or treatment for the patient?",
        context: "Outline the intended course of action or treatment. Example: The treatment plan includes prescribing medication, recommending rest, and scheduling a follow-up appointment in two weeks.",
        format: "<plan of action or treatment>, [exact quote from provider]",
      },
    ];

    // Process each section
    for (const q of questions) {
      const prompt = `
    ${q.question}

    Context: This is a doctor appointment between patient and provider. The personal identification information has been redacted. ${q.context}

    Answer Format: ${q.format}
    `;

      const llmGatewayData = {
        model: "claude-sonnet-4-5-20250929",
        messages: [
          { role: "user", content: `${prompt}\n\n{{ transcript }}` },
        ],
        transcript_id: transcriptId,
        max_tokens: 1000,
      };

      let res = await fetch("https://llm-gateway.assemblyai.com/v1/chat/completions", {
        method: "POST",
        headers: { ...headers, "Content-Type": "application/json" },
        body: JSON.stringify(llmGatewayData),
      });
      if (!res.ok) throw new Error(`Error: ${res.status}`);
      const response = await res.json();

      const result = response.choices[0].message.content;
      console.log(`${q.section}: ${q.question}`);
      console.log(result.trim());
      console.log();
    }
    ```
  </Tab>
</Tabs>
