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

# Build Headless Ambient Recorder

> Use-case tutorial: Use Headless Web SDK hooks to authenticate, create an Ambient session, and control recording in React

<Callout icon="info-circle" color="#FFE148">
  **What you will build**

  <p className="doc-tutorial-meta" aria-label="20 min, Intermediate">
    <Badge size="sm" color="gray" icon="clock">20 min</Badge> |
    <Badge size="sm" color="gray" icon="user-graduate">Intermediate</Badge>
  </p>

  * A shared platform client for your React app
  * Sign-in with Headless auth
  * An ambient session for recording
  * Start, pause, resume, and submit controls, plus session context after start
</Callout>

<Tip>
  **Using an AI coding tool?**

  Copy the following prompt to add the Suki developer documentation as a skill and [MCP server](/documentation/references/mcp) to your tool for better AI-assisted coding during the integration process. For all AI options (contextual menu, `llms.txt`, and `skill.md`), refer to [AI-optimized documentation](/documentation/references/ai-optimized-documentation).
</Tip>

<Prompt description="Install the Suki developer docs as a skill to get context on Suki's developer tools, APIs, and SDKs. Add the [MCP server](/documentation/references/mcp) for documentation search." icon="gear" iconType="regular" actions={["copy", "cursor"]}>
  Install the Suki developer docs as skill to get context on Suki's developer tools, APIs, and SDKs.

  npx skills add [https://developer.suki.ai](https://developer.suki.ai)

  Then add the Suki developer docs MCP server for access to documentation search. Follow the MCP instructions at [https://developer.suki.ai/documentation/references/mcp](https://developer.suki.ai/documentation/references/mcp).
</Prompt>

In this tutorial, you build a custom ambient recorder UI with the Headless Web SDK. You wrap the app with <strong><code>PlatformClientProvider</code></strong>, sign in with <strong><code>useAuth</code></strong>, create a session with <strong><code>useAmbient</code></strong>, and control recording with <strong><code>useAmbientSession</code></strong>.

By the end of this tutorial, you will have a React page that authenticates, creates an ambient session, and exposes start, pause, resume, and submit controls.

<Callout title="Beta" color="orange" icon="flask">
  The Headless Web SDK is in beta. APIs and docs may change. Confirm package versions with your Suki contact before production use.
</Callout>

<span id="prerequisites" />

## Prerequisites

Before you begin, make sure you have:

* Completed [Partner onboarding](/documentation/get-started/partner-onboarding).
* A React 18+ app that meets [Headless Web SDK prerequisites](/headless-web-sdk/prerequisites).
* Staging <code>partnerId</code> and <code>partnerToken</code> values.

<Note>
  Headless Web SDK is for custom UIs. If you want the hosted ambient chrome, use the [Web SDK ambient session tutorial](/documentation/tutorials/web-sdk-ambient-react) instead.
</Note>

<span id="architecture" />

## Architecture

```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFE148','primaryTextColor':'#111827','primaryBorderColor':'#D4A017','lineColor':'#6F5410','secondaryColor':'#FFF394','tertiaryColor':'#FFFADE','tertiaryTextColor':'#111827','tertiaryBorderColor':'#D4A017','arrowheadColor':'#6F5410','fontSize':'14px','edgeLabelBackground':'#FFFADE'}}}%%
flowchart LR
  A[PlatformClientProvider] --> B[useAuth]
  B --> C[useAmbient]
  C --> D[useAmbientSession]
  D --> E[Custom recorder UI]
```

<span id="project-setup" />

## Project setup

1. Use a React 18+ app that meets [Headless Web SDK prerequisites](/headless-web-sdk/prerequisites).
2. Install the required dependencies:

```shell theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
# pnpm
pnpm add @suki-sdk/platform-react

# npm
npm install @suki-sdk/platform-react
```

3. Configure your credentials:

   <Callout icon="gear" color="#929292">
     * Keep staging `partnerId` and `partnerToken` ready for sign-in.
     * Confirm beta expectations with your Suki contact before production use.
   </Callout>

<span id="tutorial-steps" />

## Tutorial steps

Work through each step in order. Read the explanation, review the code example, then continue to the next step. The full code example is available in the accordion below.

<Steps>
  <Step title="Create PlatformClient and Wrap the App">
    Create one `PlatformClient` and wrap your app with `PlatformClientProvider` so hooks share the same client.

    ```tsx theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    import { PlatformClient, PlatformClientProvider } from "@suki-sdk/platform-react";

    const client = new PlatformClient({
    env: "staging",
    enableDebug: true,
    logLevel: "error",
    });

    function App() {
    return (
    <PlatformClientProvider client={client}>
      <HeadlessAmbientRecorder />
    </PlatformClientProvider>
    );
    }
    ```
  </Step>

  <Step title="Authenticate with UseAuth">
    Call `useAuth` under the provider. When `autoRegister` is `true`, pass `providerName`, `providerOrgId`, and `providerSpecialty`.

    ```tsx theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    import { useAuth } from "@suki-sdk/platform-react";

    function AuthGate({ children }) {
    const { isLoggedIn, isPending, error, login } = useAuth({
    partnerId: "YOUR_PARTNER_ID",
    partnerToken: "YOUR_PARTNER_TOKEN",
    autoRegister: true,
    loginOnMount: true,
    providerName: "Dr. Jane Smith", // Example only — replace with the provider's full name
    providerOrgId: "YOUR_ORG_ID",
    providerSpecialty: "FAMILY_MEDICINE", // Example specialty token — replace with yours
    });

    if (isPending) return <p>Signing in...</p>;
    if (error) return <p role="alert">{error.message}</p>;
    if (!isLoggedIn) {
    return (
      <button type="button" onClick={login}>
        Sign In
      </button>
    );
    }

    return children;
    }
    ```
  </Step>

  <Step title="Create an Ambient Session with UseAmbient">
    Use `useAmbient` to create a session. Wait for `isSuccess` before you pass `ambientSessionId` into recording hooks.

    ```tsx theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    import { useEffect } from "react";
    import { useAmbient } from "@suki-sdk/platform-react";

    function SessionCreator({ onSessionReady }) {
    const {
    ambientSessionId,
    session: { create, isPending, isSuccess, error },
    } = useAmbient();

    useEffect(() => {
    create();
    }, [create]);

    useEffect(() => {
    if (isSuccess && ambientSessionId) {
      onSessionReady(ambientSessionId);
    }
    }, [isSuccess, ambientSessionId, onSessionReady]);

    if (isPending) return <p>Creating session...</p>;
    if (error) return <p role="alert">{error.message}</p>;
    return null;
    }
    ```
  </Step>

  <Step title="Start, Pause, Resume, and Submit with UseAmbientSession">
    Pass the session ID into `useAmbientSession`. After `start()`, call `setSessionContext` with patient and visit details to improve note quality. Track recording versus paused in local state. `sessionStatus` is only `created`, `submitted`, `completed`, or `cancelled`.

    ```tsx theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    import { useState } from "react";
    import { useAmbientSession } from "@suki-sdk/platform-react";

    function Recorder({ sessionId }) {
    const { start, pause, resume, submit, sessionStatus, setSessionContext } =
    useAmbientSession({ ambientSessionId: sessionId });
    // sessionStatus does not include "recording" or "paused". Track those locally.
    const [phase, setPhase] = useState<"idle" | "recording" | "paused">("idle");

    async function handleStart() {
    await start();
    setPhase("recording");
    await setSessionContext({
      // Example session context only — replace with real patient / visit metadata
      patient: { dob: "1980-01-15", sex: "M" },
      provider: { specialty: "FAMILY_MEDICINE", role: "Attending Physician" },
      visit: {
        visit_type: "Follow-up",
        encounter_type: "Office Visit",
        reason_for_visit: "Medication review",
      },
    });
    }

    async function handlePause() {
    await pause();
    setPhase("paused");
    }

    async function handleResume() {
    await resume();
    setPhase("recording");
    }

    return (
    <div>
      <p>Status: {sessionStatus}</p>
      {sessionStatus === "created" && phase === "idle" && (
        <button type="button" onClick={handleStart}>Start</button>
      )}
      {sessionStatus === "created" && phase === "recording" && (
        <>
          <button type="button" onClick={handlePause}>Pause</button>
          <button type="button" onClick={submit}>Submit</button>
        </>
      )}
      {sessionStatus === "created" && phase === "paused" && (
        <>
          <button type="button" onClick={handleResume}>Resume</button>
          <button type="button" onClick={submit}>Submit</button>
        </>
      )}
    </div>
    );
    }
    ```
  </Step>
</Steps>

<span id="full-example" />

## Full code example

<div className="doc-tutorial-full-code" data-doc-tutorial-full-code data-active-lang="react">
  <Columns cols={1}>
    <Tile href="#full-example-react">
      <span className="doc-tutorial-tile-lang-icon" data-icon="react" aria-hidden="true" />
    </Tile>
  </Columns>

  <div id="full-example-react" className="doc-tutorial-lang-panel is-active" data-lang-panel="react">
    ```tsx React theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    // Headless Web SDK Ambient recorder tutorial (React, Beta)
    // Copy this file into a React app, replace YOUR_* placeholders, then render <App />.
    // Flow: PlatformClient → PlatformClientProvider(client) → useAuth → useAmbient create → useAmbientSession

    import { useEffect, useState } from "react";
    import {
      PlatformClient,
      PlatformClientProvider,
      useAuth,
      useAmbient,
      useAmbientSession,
    } from "@suki-sdk/platform-react";

    // sessionStatus values: "created" | "submitted" | "completed" | "cancelled"
    // Track recording vs paused in local state (those are not sessionStatus values).

    // 1) One shared client for the app lifetime (module scope, not inside a component).
    const client = new PlatformClient({
      env: "staging",
      enableDebug: true,
      logLevel: "error",
    });

    function App() {
      return (
        // Pass the same client into the provider so all hooks share it.
        <PlatformClientProvider client={client}>
          <HeadlessAmbientRecorder />
        </PlatformClientProvider>
      );
    }

    function HeadlessAmbientRecorder() {
      const [sessionId, setSessionId] = useState(null);

      // 2) Sign in under the provider. When autoRegister is true, provider* fields are required.
      //    Use specialty enum tokens (for example FAMILY_MEDICINE), not display names.
      const { isLoggedIn, isPending: authPending, error: authError } = useAuth({
        partnerId: "YOUR_PARTNER_ID", // Required: from partner onboarding
        partnerToken: "YOUR_PARTNER_TOKEN", // Required
        autoRegister: true,
        loginOnMount: true,
        providerName: "Dr. Jane Smith", // Example only — replace with the provider's full name
        providerOrgId: "YOUR_ORG_ID", // Required when autoRegister is true
        providerSpecialty: "FAMILY_MEDICINE", // Example specialty token — replace with yours
      });

      // 3) Create an ambient session only after login, and only once.
      const {
        ambientSessionId,
        session: { create, isSuccess: sessionCreated, error: sessionError },
      } = useAmbient();

      useEffect(() => {
        if (isLoggedIn && !sessionCreated) {
          create();
        }
      }, [isLoggedIn, sessionCreated, create]);

      useEffect(() => {
        // Wait for isSuccess before passing ambientSessionId into useAmbientSession.
        if (sessionCreated && ambientSessionId) {
          setSessionId(ambientSessionId);
        }
      }, [sessionCreated, ambientSessionId]);

      if (authPending) return <p>Signing in...</p>;
      if (authError) return <p role="alert">{authError.message}</p>;
      if (!isLoggedIn) return <p>Sign in required</p>;
      if (sessionError) return <p role="alert">{sessionError.message}</p>;
      if (!sessionId) return <p>Creating ambient session...</p>;

      return <Recorder sessionId={sessionId} />;
    }

    function Recorder({ sessionId }) {
      // 4) Control recording for the session returned by useAmbient.
      const { start, pause, resume, submit, sessionStatus, setSessionContext } =
        useAmbientSession({ ambientSessionId: sessionId });
      // sessionStatus does not include "recording" or "paused". Track those locally.
      const [phase, setPhase] = useState<"idle" | "recording" | "paused">("idle");

      async function handleStart() {
        await start();
        setPhase("recording");
        // Call setSessionContext after start() and before submit() for better notes.
        await setSessionContext({
          // Example session context only — replace with real patient / visit metadata
          patient: { dob: "1980-01-15", sex: "M" },
          provider: { specialty: "FAMILY_MEDICINE", role: "Attending Physician" },
          visit: {
            visit_type: "Follow-up",
            encounter_type: "Office Visit",
            reason_for_visit: "Medication review",
          },
        });
      }

      async function handlePause() {
        await pause();
        setPhase("paused");
      }

      async function handleResume() {
        await resume();
        setPhase("recording");
      }

      return (
        <div>
          <p>Status: {sessionStatus}</p>
          {sessionStatus === "created" && phase === "idle" && (
            <button type="button" onClick={handleStart}>
              Start
            </button>
          )}
          {sessionStatus === "created" && phase === "recording" && (
            <>
              <button type="button" onClick={handlePause}>
                Pause
              </button>
              <button type="button" onClick={submit}>
                Submit
              </button>
            </>
          )}
          {sessionStatus === "created" && phase === "paused" && (
            <>
              <button type="button" onClick={handleResume}>
                Resume
              </button>
              <button type="button" onClick={submit}>
                Submit
              </button>
            </>
          )}
        </div>
      );
    }

    export default App;
    ```
  </div>
</div>

<span id="troubleshooting" />

## Troubleshooting

<AccordionGroup>
  <Accordion title="Hooks Fail Outside Provider">
    Wrap the tree with `PlatformClientProvider` and pass `client={client}`.
  </Accordion>

  <Accordion title="Session Create Never Runs">
    Create only when signed in and you have not already created the session for this screen.
  </Accordion>

  <Accordion title="Beta API Surprises">
    Treat this surface as beta; confirm package versions with your Suki contact.
  </Accordion>
</AccordionGroup>

<span id="summary" />

## Summary

In this tutorial, you:

* Wrapped the app with `PlatformClientProvider`.
* Signed in with `useAuth` and created a session with `useAmbient`.
* Controlled recording with `useAmbientSession` (start, pause, resume, and submit).

<span id="other-tutorials" />

## Other tutorials

Continue with another end-to-end tutorial.

<div className="hp-io-method-grid tut-hub-card-grid">
  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/tutorials/web-sdk-ambient-react">
    <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">Ambient</span>
      <h3 className="hp-io-method-card-title">Build a Web SDK Ambient Session</h3>

      <p className="hp-io-method-card-desc">
        Authenticate, initialize the Web SDK in React, and mount SukiAssistant for an ambient encounter.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="15 min, Intermediate">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">15 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/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>
</div>
