# Individual Track

Individual track recording (also known as single track recording or participant track recording) captures each participant's media separately. Instead of combining everyone into a single file like composite recording, you get isolated tracks per participant—audio-only, video-only, and/or audio+video mixes—giving you maximum flexibility for post-production.

## Quickstart

<tabs groupId="examples">

<tabs-item value="js" label="JavaScript">

```js
// Start individual recording
// Triggers webhook event: call.recording_started
await call.startRecording("individual");

// Stop individual recording
// Triggers webhook event: call.recording_stopped
// When processing completes: call.recording_ready (one event per file, contains recording URL)
await call.stopRecording("individual");

// List all recordings for this call
const response = await call.listRecordings();

// Download using the URL from response.recordings or the call.recording_ready webhook payload
// response.recordings[0].url
```

</tabs-item>

<tabs-item value="py" label="Python">

```py
# Start individual recording
# Triggers webhook event: call.recording_started
call.start_recording("individual")

# Stop individual recording
# Triggers webhook event: call.recording_stopped
# When processing completes: call.recording_ready (one event per file, contains recording URL)
call.stop_recording("individual")

# List all recordings for this call
response = call.list_recordings()

# Download using the URL from response.data.recordings or the call.recording_ready webhook payload
# response.data.recordings[0].url
```

</tabs-item>

<tabs-item value="go" label="Golang">

```go
// Start individual recording
// Triggers webhook event: call.recording_started
call.StartRecording(ctx, "individual", &getstream.StartRecordingRequest{})

// Stop individual recording
// Triggers webhook event: call.recording_stopped
// When processing completes: call.recording_ready (one event per file, contains recording URL)
call.StopRecording(ctx, "individual", &getstream.StopRecordingRequest{})

// List all recordings for this call
response, err := call.ListRecordings(ctx, &getstream.ListRecordingsRequest{})

// Download using the URL from response.Data.Recordings or the call.recording_ready webhook payload
// response.Data.Recordings[0].Url
```

</tabs-item>

<tabs-item value="curl" label="cURL">

```bash
# Start individual recording
# Triggers webhook event: call.recording_started
curl -X POST "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}/recordings/individual/start?api_key=${API_KEY}" \
  -H "Authorization: ${TOKEN}" \
  -H "stream-auth-type: jwt"

# Stop individual recording
# Triggers webhook event: call.recording_stopped
# When processing completes: call.recording_ready (one event per file, contains recording URL)
curl -X POST "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}/recordings/individual/stop?api_key=${API_KEY}" \
  -H "Authorization: ${TOKEN}" \
  -H "stream-auth-type: jwt"

# List all recordings for this call
# Download using the URL from response.recordings or the call.recording_ready webhook payload
curl "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}/recordings?api_key=${API_KEY}" \
  -H "Authorization: ${TOKEN}" \
  -H "stream-auth-type: jwt"
```

</tabs-item>

</tabs>

## How it works

When individual track recording is active, the server captures each participant's RTP packets directly. This means recordings preserve the original publish resolution and orientation—if a participant switches from landscape to portrait mid-call, the recording reflects that change. Files are encoded into MKV format (Matroska container) and uploaded to AWS S3 (Stream-managed by default, or your own bucket).

## When to use individual recording

**Advantages:**

- Per-participant tracks enable precise post-production editing and mixing
- Preserves original resolution and orientation changes during the call
- Ideal for AI/LLM processing—easier speaker attribution for transcription and analysis
- Flexible output options: audio-only, video-only, or combined audio+video per participant
- Lower cost than composite for small calls (no real-time rendering overhead)

**Limitations:**

- Multiple files to manage (up to 6 per participant)
- Requires post-processing to combine participants into a single viewable file
- Cost scales with number of participants and tracks

For ready-to-share single-file output, consider [composite recording](/video/docs/api/recording/composite/). For maximum flexibility and lowest cost, consider [raw recording](/video/docs/api/recording/raw/).

## Use cases

- **Post‑production editing:** isolate each participant for precise cuts and mixing
- **AI & LLM processing:** per‑speaker diarization, transcription, and summaries
- **Content moderation & compliance:** inspect a participant's contribution in isolation

## Output

Each recorded participant session produces files based on your `output_types` configuration. You can independently control outputs for participant camera/mic tracks and screenshare tracks:

**Participant tracks (camera/mic):**

- `audio_only` — audio‑only file
- `video_only` — video‑only file
- `audio_video` — combined audio+video file

**Screenshare tracks:**

- `screenshare_audio_only` — audio‑only file
- `screenshare_video_only` — video‑only file
- `screenshare_audio_video` — combined audio+video file

Up to 6 files per participant session (3 for camera/mic + 3 for screenshare if the participant shared their screen). One `call.recording_ready` event is emitted per generated file.

### Behavior and running state

- **start:** starts the service if not already running; rejected if already running
- **stop:** stops the service if running; rejected if already stopped
- participants are recorded automatically when they join

## Individual recording settings

These settings control whether individual recording is available or auto‑starts, and what files are produced for each participant. Configure them at the call type or override per‑call via `settings_override`.

Available settings for `individual_recording`:

- `mode`: `disabled` | `available` | `auto-on`
- `output_types`: array of strings — specifies which output files to generate

Available values for `output_types`:

| Value                     | Description                                                 |
| ------------------------- | ----------------------------------------------------------- |
| `audio_only`              | Audio-only file for participant camera/mic tracks           |
| `video_only`              | Video-only file for participant camera/mic tracks           |
| `audio_video`             | Combined audio+video file for participant camera/mic tracks |
| `screenshare_audio_only`  | Audio-only file for screenshare tracks                      |
| `screenshare_video_only`  | Video-only file for screenshare tracks                      |
| `screenshare_audio_video` | Combined audio+video file for screenshare tracks            |

**Default:** `["audio_video", "screenshare_audio_video"]`

<admonition type="info" title="Fallback behavior for audio_video outputs">

When `audio_video` or `screenshare_audio_video` is included, the system guarantees at least one file per participant who has any recorded track. If a participant doesn't have both audio and video, the system falls back to producing single-track files:

- **Participant has audio only (no video):** An `audio_only` file is produced, even if `audio_only` is not in `output_types`
- **Participant has video only (no audio):** A `video_only` file is produced, even if `video_only` is not in `output_types`

This fallback occurs because muxing audio+video requires both tracks to be present. The system ensures you always receive a recording for each participant with media, regardless of which tracks they published.

</admonition>

<tabs groupId="examples">

<tabs-item value="js" label="JavaScript">

```js
// Per‑call override — include all output types
await call.update({
  settings_override: {
    individual_recording: {
      mode: "available",
      output_types: [
        "audio_only",
        "video_only",
        "audio_video",
        "screenshare_audio_only",
        "screenshare_video_only",
        "screenshare_audio_video",
      ],
    },
  },
});

// Default for a call type — only combined audio+video outputs
await client.video.updateCallType({
  name: "<call type name>",
  settings: {
    individual_recording: {
      mode: "auto-on",
      output_types: ["audio_video", "screenshare_audio_video"],
    },
  },
});
```

</tabs-item>

<tabs-item value="py" label="Python">

```py
from getstream.models import CallSettingsRequest, IndividualRecordingSettingsRequest

# Per‑call override — include all output types
call.update(
    settings_override=CallSettingsRequest(
        individual_recording=IndividualRecordingSettingsRequest(
            mode="available",
            output_types=[
                "audio_only",
                "video_only",
                "audio_video",
                "screenshare_audio_only",
                "screenshare_video_only",
                "screenshare_audio_video",
            ],
        )
    ),
)

# Default for a call type — only combined audio+video outputs
client.video.update_call_type(
    "default",
    settings=CallSettingsRequest(
        individual_recording=IndividualRecordingSettingsRequest(
            mode="auto-on",
            output_types=["audio_video", "screenshare_audio_video"],
        )
    ),
)
```

</tabs-item>

<tabs-item value="go" label="Golang">

```go
// Per‑call override — include all output types
call.Update(ctx, &getstream.UpdateCallRequest{
  SettingsOverride: &getstream.CallSettingsRequest{
    IndividualRecording: &getstream.IndividualRecordingSettingsRequest{
      Mode: "available",
      OutputTypes: []string{
        "audio_only",
        "video_only",
        "audio_video",
        "screenshare_audio_only",
        "screenshare_video_only",
        "screenshare_audio_video",
      },
    },
  },
})

// Default for a call type — only combined audio+video outputs
client.Video().UpdateCallType(ctx, "default", &getstream.UpdateCallTypeRequest{
  Settings: &getstream.CallSettingsRequest{
    IndividualRecording: &getstream.IndividualRecordingSettingsRequest{
      Mode:        "auto-on",
      OutputTypes: []string{"audio_video", "screenshare_audio_video"},
    },
  },
})
```

</tabs-item>

<tabs-item value="curl" label="cURL">

```bash
# Per‑call override — include all output types
curl -X PATCH "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}?api_key=${API_KEY}" \
  -H "Authorization: ${TOKEN}" \
  -H "stream-auth-type: jwt" \
  -H "Content-Type: application/json" \
  -d '{
    "settings_override": {
      "individual_recording": {
        "mode": "available",
        "output_types": [
          "audio_only",
          "video_only",
          "audio_video",
          "screenshare_audio_only",
          "screenshare_video_only",
          "screenshare_audio_video"
        ]
      }
    }
  }'

# Default for a call type — only combined audio+video outputs
curl -X PUT "https://video.stream-io-api.com/api/v2/video/calltypes/${CALL_TYPE_NAME}?api_key=${API_KEY}" \
  -H "Authorization: ${TOKEN}" \
  -H "stream-auth-type: jwt" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "individual_recording": {
        "mode": "auto-on",
        "output_types": ["audio_video", "screenshare_audio_video"]
      }
    }
  }'
```

</tabs-item>

</tabs>

## Events

These events are sent to users connected to the call and your webhook/SQS, each event contains a `recording_type` field that refers to the type of recording (`individual` in this case):

- [`call.recording_started`](/video/docs/api/webhooks/events/#CallRecordingStartedEvent) when the recording has started
- [`call.recording_stopped`](/video/docs/api/webhooks/events/#CallRecordingStoppedEvent) when the recording has stopped
- [`call.recording_ready`](/video/docs/api/webhooks/events/#CallRecordingReadyEvent) when the recording is available for download (emitted once per generated file, so you may receive multiple events per participant)
- [`call.recording_failed`](/video/docs/api/webhooks/events/#CallRecordingFailedEvent) when recording fails for any reason

## User Permissions

The following permissions are checked when users interact with the recording API:

- `StartRecording` required to start the recording
- `StopRecording` required to stop the recording

## External storage

By default, individual recording files are stored in Stream‑managed S3 and retained according to your account policies. If your call type is configured to use a different storage, files will be uploaded there.


---

This page was last updated at 2026-06-23T15:36:57.745Z.

For the most recent version of this documentation, visit [https://getstream.io/video/docs/flutter/recording/individual-track/](https://getstream.io/video/docs/flutter/recording/individual-track/).