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

# Select a Microphone

> Pass an optional microphone deviceId to ambient recording with useAmbientSession, including enumeration, pause/resume switching, and failure handling

With Headless Web SDK v0.3.1, you can pass an **optional** microphone `deviceId` so ambient recording uses the mic your user picks. Your app lists devices and builds the picker UI. The Headless Web SDK does not list microphones, remember the last choice, or switch mics for you.

<Note>
  When you omit the id, recording uses the **browser default microphone**.
</Note>

### What this enables

* Choose which microphone the recording uses when you start or resume an ambient session.
* Apply the same microphone selection when a paused session resumes.
* Keep using the browser default microphone when `deviceId` is omitted.

### What this does not do

* Change the active microphone mid-recording without pause and resume.
* Switch the microphone while recording is already running, without a pause.
* Expose a public `switchMicrophone(deviceId)` API.
* Enumerate devices or persist the user’s last choice.
* Fall back to the browser default when an exact `deviceId` is unavailable.
* Accept an external `MediaStream` as the audio source.
* Support Form filling microphone selection in this release.

### When you pass a deviceId

The selected microphone is applied only when audio capture starts:

| Action | Behavior |
| - | - |
| `start()` | Uses the provided `deviceId`, or the browser default if omitted |
| `resume()` | Uses the provided `deviceId`. On Platform Client, omitting options reuses the last successful selection for that live session instance |
| Change `deviceId` while recording | Does not change the microphone already in use. Takes effect on the next `pause()` → `resume()`, or the next `start()` |

<Note>
  There is no seamless mid-session microphone switch.
</Note>

To change microphones during a visit:

* Update your app state with the new `deviceId` so `config.audio.deviceId` is current.
* Call `pause()`
* Call `resume()`

Expect a short capture gap between pause and resume.

```mermaid actions={false} theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
flowchart TD
    A[Select deviceId in host app] --> B[start or resume]
    B --> C[Recording active]
    C --> D{User changes mic?}
    D -->|No| C
    D -->|Yes| E[Update app state deviceId]
    E --> F[pause]
    F --> G[resume with new deviceId]
    G --> C

    style A fill:#FFF394,stroke:#D4A017,color:#000000
    style B fill:#FFF394,stroke:#D4A017,color:#000000
    style C fill:#FFF394,stroke:#D4A017,color:#000000
    style G fill:#FFF394,stroke:#D4A017,color:#000000
    style E fill:#e3e0e0,stroke:#8b8a8a,color:#000000
    style F fill:#e3e0e0,stroke:#8b8a8a,color:#000000
```

### Pass `deviceId` to the hook

```tsx theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
const { start, pause, resume, submit, sessionStatus } = useAmbientSession({
  ambientSessionId,
  config: selectedMicrophoneId // [!code ++:3] New in v0.3.1
    ? { audio: { deviceId: selectedMicrophoneId } }
    : undefined,
});
```

### Types

The following types show the options you can pass to the hook:

```ts theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
type AmbientMicrophoneOptions = {
  /** MediaDeviceInfo.deviceId for an audioinput device */
  deviceId?: string; // [!code ++] New in v0.3.1
};

type AmbientRecordingOptions = {
  audio?: AmbientMicrophoneOptions;
};
```

When `deviceId` is set, the recording uses `getUserMedia` with:

```ts theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
{
  audio: {
    deviceId: { exact: deviceId } // [!code ++] New in v0.3.1
  }
}
```

When `deviceId` is omitted, the browser default microphone is used.

### Enumerate devices and select a microphone

You own device enumeration and how you design and implement the microphone picker UI. Request microphone permission before you enumerate if you need readable device labels in Chromium. Changing the microphone while recording is **active** does **not switch** the microphone in use. First pause, then resume so the new device is applied.

The following code example shows how to enumerate devices and select a microphone while using the Headless Web SDK.

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

type AudioInputDevice = {
  deviceId: string;
  label: string;
};

export function AmbientRecorderWithMicPicker({
  ambientSessionId,
}: {
  ambientSessionId: string;
}) {
  const [microphones, setMicrophones] = useState<AudioInputDevice[]>([]);
  const [selectedDeviceId, setSelectedDeviceId] = useState<string>("");
  const [error, setError] = useState<string | null>(null);

  const refreshMicrophones = useCallback(async () => {
    setError(null);

    try {
      // Permission unlocks readable device labels in Chromium.
      const permissionStream = await navigator.mediaDevices.getUserMedia({
        audio: true,
        video: false,
      });
      permissionStream.getTracks().forEach((track) => track.stop());

      const devices = await navigator.mediaDevices.enumerateDevices();
      const inputs = devices
        .filter((device) => device.kind === "audioinput")
        .map((device, index) => ({
          deviceId: device.deviceId,
          label: device.label || `Microphone ${index + 1}`,
        }));

      setMicrophones(inputs);
      setSelectedDeviceId((current) => current || inputs[0]?.deviceId || "");
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    }
  }, []);

  useEffect(() => {
    void refreshMicrophones();
  }, [refreshMicrophones]);

  const { start, pause, resume, submit, sessionStatus } = useAmbientSession({
    ambientSessionId,
    config: selectedDeviceId // [!code ++:5] New in v0.3.1
      ? {
          audio: { deviceId: selectedDeviceId },
        }
      : undefined,
  });

  const changeMicrophoneDuringRecording = async (nextDeviceId: string) => {
    setSelectedDeviceId(nextDeviceId);

    // Changing deviceId while recording does not switch the microphone in use.
    // Pause, then resume so the new device is applied.
    await pause();
    await resume();
  };

  return (
    <div>
      <label>
        Microphone
        <select
          value={selectedDeviceId}
          onChange={(event) => {
            const nextDeviceId = event.target.value;
            void changeMicrophoneDuringRecording(nextDeviceId);
          }}
        >
          {microphones.map((mic) => (
            <option key={mic.deviceId} value={mic.deviceId}>
              {mic.label}
            </option>
          ))}
        </select>
      </label>

      <button type="button" onClick={() => void refreshMicrophones()}>
        Refresh devices
      </button>

      {error ? <p>{error}</p> : null}

      <p>Status: {sessionStatus}</p>
      <button type="button" onClick={() => void start()}>
        Start
      </button>
      <button type="button" onClick={() => void pause()}>
        Pause
      </button>
      <button type="button" onClick={() => void resume()}>
        Resume
      </button>
      <button type="button" onClick={() => void submit()}>
        Submit
      </button>
    </div>
  );
}
```

### What your app handles

* List microphones in your app with `enumerateDevices()`, and keep only `audioinput` devices. The SDK does not list them for you.
* Ask for microphone permission before you list devices if you need the names to be readable.
* Save the chosen `deviceId` in your app. The SDK keeps it in memory only and does not save it to IndexedDB. Pass `config.audio` again after a page reload, an SDK remount, or session recovery.
* If the user picks a new microphone while recording, the current microphone stays in use until you call `pause()`, then `resume()`. The hook may log a warning until that `resume()` succeeds.

### When the device is unavailable

When you pass an exact `deviceId` and that device is missing, disconnected, or otherwise unavailable, `getUserMedia` fails. The Headless Web SDK does not automatically fall back to the browser default microphone.

Mic capture failures are **not** a top-level `AudioRecorderFailure` error. They are wrapped as session lifecycle errors:

| Path | Partner-facing `code` | `reason` |
| - | - | - |
| Failed on `start()` / `startAmbientSession` | `StartSessionFailed` | `AudioRecorderFailure` |
| Failed on `resume()` / `resumeAmbientSession` | `ResumeSessionFailed` | `AudioRecorderFailure` |

<CardGroup cols={2}>
  <Card title="Platform Client" icon="code">
    You receive a structured error with `code`, `reason`, and `cause`. `cause` is the browser microphone error from `getUserMedia`.
  </Card>

  <Card title="React Hook" icon="react">
    `start()` and `resume()` throw a string, not an object with `error.code` and `error.reason`.

    ```text theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
    [AmbientSession] Code: StartSessionFailed | Reason: AudioRecorderFailure | Message: Failed to start audio recorder in startSession. ...
    ```
  </Card>
</CardGroup>

In your UI, ask the user to pick another microphone, refresh the device list, then call `start()` or `resume()` again. [Starting a session](/headless-web-sdk/guides/error-handling#starting-a-session-startsessionfailed) and [Resuming a session](/headless-web-sdk/guides/error-handling#resuming-a-session-resumesessionfailed) list the same `code` and `reason`.

**Example code**: Handle start failure

```tsx theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
try {
  await start();
} catch (err) {
  // Platform: check error.code === "StartSessionFailed" && error.reason === "AudioRecorderFailure"
  // React: err is often a string that includes Code: StartSessionFailed | Reason: AudioRecorderFailure
  console.error("Failed to start ambient recording", err);
  await refreshMicrophones();
}
```

<Warning>
  Changing `config.audio.deviceId` while recording does not switch the microphone that is already in use. The React hook may log a diagnostic warning. The new device applies on the next successful `start()` or `resume()`.
</Warning>

For Platform Client (`startAmbientSession` / `resumeAmbientSession`) option retention, including `{ audio: undefined }` to clear a previous in-memory selection, refer to [Platform client and provider](/headless-web-sdk/api-reference/platform-client#startambientsession).

### Partner checklist

<Accordion title="See all the checklist items">
  * Enumerate `audioinput` devices in your app.
  * Store the selected `deviceId` in your app state.
  * Pass it through `useAmbientSession` `config.audio` or Platform start/resume options.
  * On mic change during an active recording, pause then resume.
  * Re-supply `deviceId` after remount or recovery.
  * Handle start/resume failures when the selected device is unavailable.
  * Do not expect the microphone to change while recording is active, and do not expect a `switchMicrophone` API in this release.
</Accordion>

### Microphone FAQs

<AccordionGroup>
  <Accordion title="Does Changing the Dropdown While Recording Switch the Mic Immediately?">
    No. The microphone that is already recording does not change. Pause, then resume with the new `deviceId`.
  </Accordion>

  <Accordion title="Will the SDK Remember the Selected Mic After a Page Reload?">
    No. Selection is in-memory only. Persist the preference in your app if needed, then pass it again on start/resume after remount.
  </Accordion>

  <Accordion title="What Happens If the Selected Device Is Unplugged?">
    Start or resume fails. The SDK does not fall back to another microphone automatically. The error uses `code: "StartSessionFailed"` or `code: "ResumeSessionFailed"` with `reason: "AudioRecorderFailure"`. On React, you may receive that information as a string from `error.toString()`.
  </Accordion>

  <Accordion title="Is Form Filling Covered?">
    No. Form filling `deviceId` support is out of scope for this release.
  </Accordion>

  <Accordion title="Is There a switchMicrophone API?">
    Not in this release. Use pause → resume with the new `deviceId`.
  </Accordion>
</AccordionGroup>

## Next steps

<Icon icon="file-lines" iconType="solid" /> See working microphone and recording flows in [Ambient session examples](/headless-web-sdk/guides/hooks/ambient-session-examples).

<Icon icon="file-lines" iconType="solid" /> Review `config.audio.deviceId` and related options in [Configure ambient session](/headless-web-sdk/guides/hooks/ambient-session-config).

<Icon icon="file-lines" iconType="solid" /> Refer to the [Manage ambient session](/headless-web-sdk/guides/hooks/ambient-session-hook) guide for start, pause, and resume use cases.

<Icon icon="file-lines" iconType="solid" /> Call `startAmbientSession` and `resumeAmbientSession` with microphone options in [Platform client and provider](/headless-web-sdk/api-reference/platform-client#startambientsession).

<Icon icon="file-lines" iconType="solid" /> Learn how to handle start and resume mic failures in the [Error handling guide](/headless-web-sdk/guides/error-handling).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.