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

# Capture a Patient Visit

> Design the recorder UI and sequence create, context, WebSocket streaming, Stop, End, and content retrieval for custom Ambient experiences

<div className="quick-summary-wrapper">
  <div className="quick-summary-header">
    <span className="quick-summary-icon" aria-hidden="true" />

    <span className="quick-summary-title">Quick summary</span>
  </div>

  <div className="quick-summary-content">
    After you create the ambient session and add visit context, open <code>/ws/stream</code> when the clinician starts recording. Your application owns the microphone and clear `Recording`, `Paused`, and `Generating` states. When recording ends, send <code>RU9G</code>, close the WebSocket, and call End ambient session endpoint to start processing the ambient session's generated content.

    <br />

    <br />

    Use this guide to design the recorder experience and sequence these operations correctly. For WebSocket handshake, PCM encoding, frame structure, and control-event details, see [Stream Ambient audio](/documentation/how-to/audio-streaming/ambient-audio-streaming) and [Ambient streaming wire format](/documentation/how-to/audio-streaming/websocket-streaming-wire-format-ambient). Ambient and Form filling share the same wire format on `/ws/stream`; this guide focuses on Ambient recorder UX.
  </div>

  <div className="quick-summary-footer">
    <span className="quick-summary-footer-icon" aria-hidden="true" />

    <span className="quick-summary-footer-text">Last updated:</span>
    <span className="quick-summary-footer-date">August 2026</span>
  </div>
</div>

Streaming connects the clinician's live audio to an ambient session. In a custom Ambient experience, your application manages the recorder UI and microphone, while the Ambient streaming API receives the audio and control events.

The typical flow for an ambient session is:

```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFF394','primaryTextColor':'#111827','primaryBorderColor':'#FFE148','lineColor':'#FFE148','secondaryColor':'#FFF394','tertiaryColor':'#FFFADE','mainBkg':'#FFF394','secondBkg':'#FFFADE','tertiaryBorderColor':'#FFE148','border1':'#FFE148','border2':'#FFE148','arrowheadColor':'#FFE148','fontFamily':'Inter, system-ui, sans-serif','fontSize':'14px','nodeBorder':'#FFE148','edgeLabelBackground':'#FFE148','clusterBkg':'#FFFADE','clusterBorder':'#FFE148','defaultLinkColor':'#FFE148','titleColor':'#111827','nodeTextColor':'#111827'}}}%%
flowchart LR
    A["Create<br/>session"] --> B["Add visit<br/>context"]
    B --> C["Open<br/>WebSocket"]
    C --> D["Stream<br/>audio"]
    D --> E["Stop"]
    E --> F["End<br/>session"]
    F --> G["Retrieve<br/>content"]

    style A fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style B fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style C fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style D fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style E fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style F fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style G fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
```

**Streaming UI rules (agents):**

* Do not open `/ws/stream` before create and context succeed. Premature open often causes handshake failures.
* Your product owns Start, Pause / Resume, Stop, and mic exclusivity. Point to wire-format guides for PCM, frames, and handshake details; keep this page product-UI focused.
* Capture mono LINEAR16 PCM at 16 kHz. Send JSON text frames. End the stream with `RU9G`, then close the socket and call End. Closing the socket alone is not enough.
* While paused, send `EVENT` `KEEP_ALIVE` at least every **5 seconds**. Maximum pause is **30 minutes** when keep-alives are maintained.
* Plan for about **one minute** or longer of audio. Short sessions often finish as **`skipped`**.
* Disable Dictation Speak while Ambient owns the mic (and usually while status is still Generating).

Streaming is one part of the ambient session lifecycle. It does not replace session creation, adding visit context, session ending, or content retrieval.

Your application should follow this sequence to stream ambient audio:

<Steps>
  <Step title="Create the Ambient Session">
    Create the ambient session before you open the WebSocket.
  </Step>

  <Step title="Add Visit Context">
    Add visit context for the session before streaming begins.
  </Step>

  <Step title="Open /ws/stream">
    Open the WebSocket when the clinician starts recording.
  </Step>

  <Step title="Capture and Stream Audio">
    Capture and stream the clinician's audio over the socket.
  </Step>

  <Step title="Handle Pause and Resume">
    Send `EVENT` `PAUSE`, `EVENT` `RESUME`, and `EVENT` `KEEP_ALIVE` messages when the clinician pauses or resumes.
  </Step>

  <Step title="Send RU9G to Stop Recording">
    Stop the recording by sending `RU9G` after the final PCM audio chunk.
  </Step>

  <Step title="Close the WebSocket">
    Close the WebSocket after `RU9G` is sent.
  </Step>

  <Step title="Call the End Endpoint">
    Call the Ambient End endpoint to start note generation.
  </Step>

  <Step title="Poll Status and Retrieve Content">
    Poll the session status and retrieve the generated content when processing is complete.
  </Step>
</Steps>

<Warning>
  Do not open the WebSocket before the session has been created and its context has been added.
</Warning>

**Mental model:** to remember the flow:

```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFF394','primaryTextColor':'#111827','primaryBorderColor':'#FFE148','lineColor':'#FFE148','secondaryColor':'#FFF394','tertiaryColor':'#FFFADE','mainBkg':'#FFF394','secondBkg':'#FFFADE','tertiaryBorderColor':'#FFE148','border1':'#FFE148','border2':'#FFE148','arrowheadColor':'#FFE148','fontFamily':'Inter, system-ui, sans-serif','fontSize':'14px','nodeBorder':'#FFE148','edgeLabelBackground':'#FFE148','clusterBkg':'#FFFADE','clusterBorder':'#FFE148','defaultLinkColor':'#FFE148','titleColor':'#111827','nodeTextColor':'#111827'}}}%%
flowchart LR
    A[Create session] --> B[Add visit context]
    B --> C[Open /ws/stream]
    C --> D[Stream audio]
    D --> E[RU9G]
    E --> F[Close WebSocket]
    F --> G[End session]
    G --> H[Poll status]
    H --> I[Retrieve content]

    style A fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style B fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style C fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style D fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style E fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style F fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style G fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style H fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
    style I fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
```

For the complete streaming protocol, see the following guides:

* [Audio streaming API](/api-reference/ambient-sessions/audio-stream)
* [Stream Ambient audio](/documentation/how-to/audio-streaming/ambient-audio-streaming)
* [Ambient streaming wire format](/documentation/how-to/audio-streaming/websocket-streaming-wire-format-ambient)

## Design the recorder experience

A custom ambient experience should make the recording state clear at every point. The clinician should always be able to tell whether audio is being captured.

At minimum, provide:

* **Start** to begin ambient recording indicating that the ambient session is active.
* **Pause** to temporarily stop capturing audio and show in the UI that the ambient session is paused.
* **Resume** to continue the same ambient session. If you are showing a timer, resume the timer when the ambient session is resumed.
* **Stop** to finish recording and start processing. Show in the UI that the ambient session is generating content.

A typical state transition looks like this:

| Clinician action | UI state   | Streaming operation                                              |
| :--------------- | :--------- | :--------------------------------------------------------------- |
| Start Ambient    | Recording  | Create session, add context, open `/ws/stream`, stream PCM audio |
| Pause            | Paused     | Send `EVENT` `PAUSE` and continue `EVENT` `KEEP_ALIVE` messages  |
| Resume           | Recording  | Send `EVENT` `RESUME` on the same session                        |
| Stop             | Generating | Send `RU9G`, close the socket, call End, and poll status         |

### Show the recording state clearly

Use a persistent recording indicator while the ambient session is capturing audio. When the clinician pauses, make the paused state equally clear.
After Stop, move the UI to a **Generating** state rather than leaving the clinician in a **Recording** state while ambient content is processed.

### Keep microphone ownership clear

Do not stream Ambient and Dictation audio streams from the same device microphone at the same time. The two experiences use different streaming contracts and can contend for microphone access.

Disable Dictation microphone while ambient session owns the microphone. Depending on your application flow, you may also keep it disabled while the ambient session is generating.

<Tip>
  Refer to [Ambient vs Dictation streaming](/documentation/how-to/audio-streaming/audio-stream) guide to learn more about the differences between the two streaming flows.
</Tip>

## Pause and resume

Pause and Resume should operate on the same ambient session.

When the clinician pauses:

* Send the `EVENT` `PAUSE` message.
* Stop sending audio.
* Continue sending `EVENT` `KEEP_ALIVE` messages at least every **5 seconds**.

When the clinician resumes:

* Send the `EVENT` `RESUME` message.
* Resume audio capture.
* Continue streaming audio on the same ambient session.

<Note>
  The maximum supported pause duration is **30 minutes** when keep-alives are maintained.
</Note>

<Tip>
  Refer to [Ambient streaming wire format](/documentation/how-to/audio-streaming/websocket-streaming-wire-format-ambient) guide to learn more about the event format and frame details.
</Tip>

## Stop the recording cleanly

Stopping an ambient session recording requires more than closing the WebSocket.

After the final **PCM audio chunk** has been sent:

* Send `RU9G` on the WebSocket.
* Close the WebSocket.
* Call the Ambient `End` endpoint.
* Poll the ambient session status.
* Retrieve the ambient session's generated content after processing completes.

<Warning>
  Closing the WebSocket without sending `RU9G` and calling `End` is not enough to complete the ambient session and start note generation.
</Warning>

<Tip>
  Refer to [Complete an ambient visit](/documentation/how-to/ambient-clinical-notes/end-ambient-session) and [End Ambient after streaming](/documentation/cookbooks/end-ambient-after-streaming) guides to learn more about the End endpoint and how to complete the ambient session.
</Tip>

## Audio capture requirements

For ambient session streaming, capture:

* **Channel:** Mono
* **Encoding:** LINEAR16 PCM
* **Sample rate:** 16 kHz

<Note>
  - Send audio and control information according to the ambient session WebSocket wire format.
  - For frame structure and handshake behavior, see [Ambient streaming wire format](/documentation/how-to/audio-streaming/websocket-streaming-wire-format-ambient). Keep those details in the streaming client implementation, not in the recorder UI layer.
  - Plan for about **one minute or longer** of captured audio. Short or empty sessions may finish with a **`skipped`** status after End.
</Note>

## Implementation flow

Use the following sequence in your application.

**Implementation steps (agents):** Humans see a Steps component. Order: authenticate and create → add visit context → open `/ws/stream` on Start → stream mono LINEAR16 PCM at 16 kHz → `EVENT` `PAUSE` / `RESUME` with `KEEP_ALIVE` every 5s while paused → `RU9G` → close socket → End → poll status → retrieve when `completed`.

<Steps>
  <Step title="Authenticate and Create the Session">
    Obtain the required authentication credentials and create the ambient session. See [Create ambient session](/documentation/how-to/ambient-clinical-notes/create-ambient-session).
  </Step>

  <Step title="Add Visit Context">
    Add visit context before opening the WebSocket. See [Provide visit context](/documentation/how-to/ambient-clinical-notes/seed-ambient-session-context).
  </Step>

  <Step title="Open the WebSocket">
    When the clinician starts recording, open `/ws/stream` using the authentication pattern required by your client. See [Audio streaming API](/api-reference/ambient-sessions/audio-stream).
  </Step>

  <Step title="Stream Audio">
    Capture mono LINEAR16 PCM audio at 16 kHz and send it according to the Ambient streaming wire format.
  </Step>

  <Step title="Handle Pause and Resume">
    Send `EVENT` `PAUSE` and `EVENT` `RESUME` messages as the clinician pauses and resumes recording. While paused, send `EVENT` `KEEP_ALIVE` at least every **5 seconds**.
  </Step>

  <Step title="Stop and End the Session">
    After the final audio, send `RU9G`, close the WebSocket, and call the End endpoint.
  </Step>

  <Step title="Wait for Processing">
    Poll the ambient session status and retrieve the generated content after processing completes. See [Check note status](/documentation/how-to/ambient-clinical-notes/handle-ambient-session-status).
  </Step>
</Steps>

## What belongs in your application

Keep the responsibilities separated between your recorder UI and streaming client.

<CardGroup cols={2}>
  <Card title="Recorder UI" icon="display">
    Your application is responsible for:

    * Start, Pause, Resume, and Stop controls.
    * Showing whether Ambient is Recording, Paused, or Generating.
    * Managing microphone access.
    * Preventing conflicting microphone use with Dictation.
    * Moving the UI from Recording to Generating after Stop.
  </Card>

  <Card title="Streaming Client" icon="waveform">
    Your streaming implementation is responsible for:

    * Opening `/ws/stream` at the correct point in the session lifecycle.
    * Capturing and encoding audio in the required format.
    * Sending audio frames.
    * Sending control events (`EVENT` messages for pause, resume, and keep-alive).
    * Maintaining keep-alives while paused.
    * Sending `RU9G` before closing the WebSocket.

    For protocol details, see [Ambient streaming wire format](/documentation/how-to/audio-streaming/websocket-streaming-wire-format-ambient).
  </Card>
</CardGroup>

## Common mistakes to avoid

**Common mistakes accordions (agents):** Humans expand one panel at a time.

* **Opening the WebSocket Too Early:** Create session and add context before `/ws/stream`.
* **Treating Socket Close as the End of the Session:** Send `RU9G`, close socket, call End.
* **Stopping Keep-Alives While Paused:** Send `EVENT` `KEEP_ALIVE` every 5 seconds while paused.
* **Letting Ambient and Dictation Share the Microphone:** One mic owner at a time.
* **Hiding the Recording State:** Show Recording, Paused, and Generating clearly.
* **Ending Very Short Sessions:** Plan for about one minute of audio; short sessions may be `skipped`.

<AccordionGroup>
  <Accordion title="Opening the WebSocket Too Early" icon="triangle-exclamation">
    Do not open `/ws/stream` before the ambient session has been created and visit context has been added. Open it as part of the Start Ambient flow.
  </Accordion>

  <Accordion title="Treating Socket Close as the End of the Session" icon="ban">
    Closing the WebSocket is not the complete Stop flow. Send `RU9G`, close the socket, and call the Ambient End endpoint.
  </Accordion>

  <Accordion title="Stopping Keep-Alives While Paused" icon="clock">
    Do not stop the keep-alive mechanism during a pause. Send `EVENT` `KEEP_ALIVE` at least every **5 seconds** while paused.
  </Accordion>

  <Accordion title="Letting Ambient and Dictation Share the Microphone" icon="microphone">
    Do not stream Ambient and Dictation from the same device microphone at the same time. Keep microphone ownership explicit in your application.
  </Accordion>

  <Accordion title="Hiding the Recording State" icon="display">
    Always make Recording, Paused, and Generating states visible. The clinician should be able to tell whether Ambient is actively listening.
  </Accordion>

  <Accordion title="Ending Very Short Sessions" icon="timer">
    Plan for about **one minute or longer** of audio. Short or empty sessions may finish with a **`skipped`** status.
  </Accordion>
</AccordionGroup>

## Implementation checklist

<Note>
  Before shipping a custom Ambient recorder, verify that your application:

  * Creates the ambient session before opening `/ws/stream`.
  * Adds visit context before streaming begins.
  * Clearly shows Recording, Paused, and Generating states.
  * Supports Pause and Resume on the same ambient session.
  * Sends `EVENT` `KEEP_ALIVE` at least every **5 seconds** while paused.
  * Keeps Ambient and Dictation from using the same microphone at the same time.
  * Captures mono LINEAR16 PCM at 16 kHz.
  * Sends `RU9G` after the final audio.
  * Closes the WebSocket after sending `RU9G`.
  * Calls the Ambient End endpoint after closing the socket.
  * Polls session status after End.
  * Handles the **`skipped`** status for short or empty sessions.
</Note>

## Related guides

<CardGroup cols={2}>
  <Card title="Stream Ambient Audio" icon="waveform" href="/documentation/how-to/audio-streaming/ambient-audio-streaming" arrow={true}>
    Learn how to connect to the Ambient WebSocket and stream audio.
  </Card>

  <Card title="Ambient Streaming Wire Format" icon="code" href="/documentation/how-to/audio-streaming/websocket-streaming-wire-format-ambient" arrow={true}>
    Learn about WebSocket frames, PCM encoding, control events, and the streaming protocol.
  </Card>

  <Card title="Audio Capture Best Practices" icon="microphone" href="/documentation/how-to/audio-streaming/audio-capture-best-practices" arrow={true}>
    Review audio capture guidance for streaming clients.
  </Card>

  <Card title="Ambient vs Dictation Streaming" icon="code-compare" href="/documentation/how-to/audio-streaming/audio-stream" arrow={true}>
    Understand the differences between Ambient and Dictation streaming.
  </Card>
</CardGroup>

## Available cookbooks

<div className="hp-io-method-grid tut-hub-card-grid" data-cookbook-related-grid>
  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/cookbooks/end-ambient-after-streaming">
    <div className="tut-hub-card-media" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <div className="tut-hub-card-badges">
        <span className="hp-wn-badge hp-wn-badge-new">Ambient</span>
        <span className="hp-wn-badge cookbook-hub-badge-surface cookbook-hub-badge-surface--api">API</span>
      </div>

      <h3 className="hp-io-method-card-title">End Ambient After Streaming</h3>

      <p className="hp-io-method-card-desc cookbook-hub-card-desc">
        Send RU9G, then end session.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="5 min">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">5 min</span>
        </div>
      </div>
    </div>
  </a>

  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/cookbooks/browser-websocket-auth">
    <div className="tut-hub-card-media tut-hub-card-media--blue" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <div className="tut-hub-card-badges">
        <span className="hp-wn-badge hp-wn-badge-new">Ambient</span>
        <span className="hp-wn-badge cookbook-hub-badge-surface cookbook-hub-badge-surface--api">API</span>
      </div>

      <h3 className="hp-io-method-card-title">Authenticate Browser WebSocket Handshake</h3>

      <p className="hp-io-method-card-desc cookbook-hub-card-desc">
        Auth browser WebSocket with protocols.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="5 min">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">5 min</span>
        </div>
      </div>
    </div>
  </a>

  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/cookbooks/wait-for-status-before-retrieve">
    <div className="tut-hub-card-media" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <div className="tut-hub-card-badges">
        <span className="hp-wn-badge hp-wn-badge-new">Ambient</span>
        <span className="hp-wn-badge cookbook-hub-badge-surface cookbook-hub-badge-surface--api">API</span>
      </div>

      <h3 className="hp-io-method-card-title">Poll Session Status Before Fetching Content</h3>

      <p className="hp-io-method-card-desc cookbook-hub-card-desc">
        Poll status until completed, then retrieve.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="5 min">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">5 min</span>
        </div>
      </div>
    </div>
  </a>
</div>

## Available tutorials

<div className="hp-io-method-grid tut-hub-card-grid">
  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/tutorials/ambient-websocket-code-example">
    <div className="tut-hub-card-media" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <span className="hp-wn-badge hp-wn-badge-new">Ambient</span>
      <h3 className="hp-io-method-card-title">Build an Ambient Streaming Client</h3>

      <p className="hp-io-method-card-desc">
        Authenticate, create a session, stream PCM audio over WebSocket, and retrieve clinical note results.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="20 min, Intermediate">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">20 min</span>
          <span className="tut-hub-level">Intermediate</span>
        </div>
      </div>
    </div>
  </a>

  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/tutorials/headless-ambient-hooks">
    <div className="tut-hub-card-media tut-hub-card-media--blue" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <span className="hp-wn-badge hp-wn-badge-new">Headless Web SDK</span>
      <h3 className="hp-io-method-card-title">Build Headless Ambient Recorder</h3>

      <p className="hp-io-method-card-desc">
        Use Headless Web SDK hooks to authenticate, create an ambient session, and control recording in React.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="20 min, Intermediate">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">20 min</span>
          <span className="tut-hub-level">Intermediate</span>
        </div>
      </div>
    </div>
  </a>
</div>

## What's next

<Icon icon="file-lines" iconType="solid" /> **[Audio streaming API](/api-reference/ambient-sessions/audio-stream)**
WebSocket endpoint reference for Ambient audio streaming.

<Icon icon="file-lines" iconType="solid" /> **[Stream Ambient audio](/documentation/how-to/audio-streaming/ambient-audio-streaming)**
End-to-end streaming implementation guide.

<Icon icon="file-lines" iconType="solid" /> **[Complete an ambient visit](/documentation/how-to/ambient-clinical-notes/end-ambient-session)**
End the ambient session after recording.

<Icon icon="file-lines" iconType="solid" /> **[Check note status](/documentation/how-to/ambient-clinical-notes/handle-ambient-session-status)**
Monitor processing after the session ends.
