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

# Headless Web SDK Integration Patterns

> Learn the best practices to implement recommended pause and submit patterns for useAmbientSession, and avoid mistakes that can discard audio or conflict with the Headless Web SDK

<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">
    Use this guide to implement recommended patterns for pausing and submitting sessions with the `useAmbientSession` hook. By following these patterns, you ensure that Suki handles audio data correctly and you avoid conflicts with the Headless Web SDK.
  </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">June 2026</span>
  </div>
</div>

The `useAmbientSession` hook provides the `pause()`, `setSessionContext()`, `submit()`, and `cancel()` methods. The Headless Web SDK manages buffering, queuing, and upload orchestration for you.

If you add custom recovery logic, you might accidentally discard valid audio or call methods in an incorrect order. Follow this guide to implement recommended patterns for pausing and submitting sessions with the `useAmbientSession` hook. By following these patterns, you ensure that Suki handles audio data correctly and you avoid conflicts with the Headless Web SDK.

## Built-in behavior

The Headless Web SDK manages buffering, queuing, and upload orchestration for you. You do not need to re-implement these features in your application:

* **Offline and network drops** - When the session is offline, the Headless Web SDK persists audio (for example in `IndexedDB`) and manages the upload queue. For details, refer to [Network transitions](/headless-web-sdk/guides/online-offline-behavior) and [Offline mode](/headless-web-sdk/guides/offline-mode) for more information.
* **Finalization with `submit()`** - Use `submit()` to complete the session and send captured audio to the Suki backend server for processing.
* **Recoverable pause errors** - A failed `pause()` call does not always mean session data or buffered audio is lost. Refer to [Pausing a session (PauseSessionFailed)](/headless-web-sdk/guides/error-handling#pausing-a-session-pausesessionfailed) in the error handling guide for more information. This guide covers how to handle these errors.

## Common mistakes to avoid

The following are common mistakes to avoid when using the `useAmbientSession` hook.

<AccordionGroup>
  <Accordion title="Retrying pause() from stale state">
    `sessionStatus` reflects the state of the last React render. If you call `pause()` again because the status has not updated yet, the API may return an `InvalidSessionState` error.

    **Instead:** Call `pause()` once. If the request fails, log a warning and stop. Do not use `sessionStatus` as an immediate source of truth right after an `await pause()` call.
  </Accordion>

  <Accordion title="Using cancel() as a fallback">
    The `cancel()` method is only for when a user explicitly abandons an encounter. It tears down the session and clears locally captured audio.

    **Instead:** Do not call `cancel()` in a `catch` block to recover from non-abandon errors. That pattern can lead to lost submissions during offline recording.
  </Accordion>

  <Accordion title="Hiding errors from destructive actions">
    If you use a destructive fallback like `cancel()` and ignore the failure, your UI may drift away from the actual SDK session state.

    **Instead:** If you perform a destructive action, always surface failures to the user or your logging pipeline.
  </Accordion>
</AccordionGroup>

## Recommended flow

Follow this flow so your integration stays aligned with the Headless Web SDK:

<Steps>
  <Step title="Pause once">
    Call `pause()` a single time. On failure, log the error and stop.
  </Step>

  <Step title="Set context (best effort)">
    Use `setSessionContext()` when you have fresh visit metadata. You can still call `submit()` when your product rules allow it, even if this step fails.
  </Step>

  <Step title="Submit to finalize">
    Call `submit()` to upload queued chunks and complete the session.
  </Step>

  <Step title="Reserve cancel() for discard">
    Only call `cancel()` when the user confirms they want to abandon the session.
  </Step>
</Steps>

## Code example

This example wraps `pause`, `setSessionContext`, and `submit` in a helper hook. Types follow [Session context](/headless-web-sdk/api-reference/types/session-context) and [UseAmbientSessionReturn](/headless-web-sdk/api-reference/types/use-ambient-session-return). Swap in your own `sessionContext`, `shouldSkipSubmit`, and callbacks.

```tsx React expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import { useCallback } from "react";

// Matches the SessionContext fields you send with setSessionContext (see API reference).
type SessionContext = {
  patient?: {
    dob: string;
    sex: string;
  };
  provider?: {
    specialty: string;
    role?: string;
  };
  visit?: {
    visit_type?: string;
    encounter_type?: string;
    reason_for_visit?: string;
    chief_complaint?: string;
  };
  sections?: Array<{ loinc: string }>;
  diagnoses?: {
    values: Array<{
      codes: Array<{
        code: string;
        description: string;
        type: string;
      }>;
      diagnosisNote: string;
    }>;
  };
};

// Empty success payload from setSessionContext; failures throw (see Session context types).
type SessionContextResponse = Record<string, never>;

// Methods and data passed in from your component that already called useAmbientSession.
type HandlersProps = {
  pause: () => Promise<void>;
  submit: () => Promise<void>;
  setSessionContext: (ctx: SessionContext) => Promise<SessionContextResponse>;
  sessionContext: SessionContext;
  shouldSkipSubmit: boolean;
  onSubmitSuccess: () => void;
  onSubmitError: (error: unknown) => void;
};

export function useAmbientSessionHandlers({
  pause,
  submit,
  setSessionContext,
  sessionContext,
  shouldSkipSubmit,
  onSubmitSuccess,
  onSubmitError,
}: HandlersProps) {
  const processSessionPause = useCallback(async () => {
    try {
      // One attempt only. Do not branch on stale sessionStatus and call pause() again.
      await pause();
    } catch (pauseError) {
      // Recoverable in many cases (for example stream close during a blip). Do not cancel() here.
      console.warn("[Suki] pause() failed", pauseError);
    }
  }, [pause]);

  const handleSubmit = useCallback(async () => {
    if (shouldSkipSubmit) {
      return;
    }

    try {
      // Best-effort metadata before finalize; still call submit() when your rules allow.
      await setSessionContext(sessionContext);
    } catch (contextError) {
      console.warn("[Suki] setSessionContext failed", contextError);
    }

    try {
      // Authoritative finalize path: SDK uploads queued audio, including offline-captured chunks.
      await submit();
      onSubmitSuccess();
    } catch (error) {
      onSubmitError(error);
    }
  }, [
    shouldSkipSubmit,
    setSessionContext,
    sessionContext,
    submit,
    onSubmitSuccess,
    onSubmitError,
  ]);

  return { processSessionPause, handleSubmit };
}
```

Pass the methods from `useAmbientSession` into the helper:

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

// ambientSessionId comes from useAmbient after session.create(); add onAudioChunkAvailable, audioBatchSize, etc. as needed.
const { pause, submit, setSessionContext } = useAmbientSession({
  ambientSessionId,
  // ...config, callbacks
});

const { processSessionPause, handleSubmit } = useAmbientSessionHandlers({
  pause,
  submit,
  setSessionContext,
  sessionContext: mySessionContext,
  shouldSkipSubmit: false,
  onSubmitSuccess: () => setSubmitted(true),
  onSubmitError: (err) => setError(String(err)),
});
```

## Next steps

<Icon icon="file-lines" iconType="solid" /> Learn how to map errors to fixes in the [Error handling](/headless-web-sdk/guides/error-handling) guide.

<Icon icon="file-lines" iconType="solid" /> Understand offline sessions in [Offline mode](/headless-web-sdk/guides/offline-mode).
