# Recording

Capture and store call recordings for compliance, quality assurance, or reference.

## Best Practices

- Check user has recording permission before showing the button.
- Show loading state - recording takes a few moments to start.
- Listen for `call.recording_started`/`call.recording_stopped` events to update UI.
- Wait for `call.recording_ready` event before fetching recordings (~30+ seconds after stop).
- Use `listRecordings()` to list recordings; optionally filter by `callSessionId`.

## Recording calls

The `Call` object exposes the call recording API. To start recording, we simply invoke `call.startRecording()`. To stop recording, we use `call.stopRecording()`.
To determine, whether a call recording is in progress, we use `useIsCallRecordingInProgress` hook. This may serve us well when we want to provide visual clues about the recording state. We have to be aware, that it can take a few moments until a call recording starts. We recommend creating a state for signaling that the call recording is starting, but has not begun yet.

```tsx
import { useCallback, useEffect, useState } from "react";
import { useCall, useCallStateHooks } from "@stream-io/video-react-sdk";

import { LoadingIndicator } from "../LoadingIndicator";

export const CustomCallRecordButton = () => {
  const call = useCall();
  const { useIsCallRecordingInProgress } = useCallStateHooks();
  const isCallRecordingInProgress = useIsCallRecordingInProgress();
  const [isAwaitingResponse, setIsAwaitingResponse] = useState(false);

  useEffect(() => {
    if (!call) return;
    // we wait until call.recording_started/stopped event
    // to remove the loading indicator
    const eventHandlers = [
      call.on("call.recording_started", () => setIsAwaitingResponse(false)),
      call.on("call.recording_stopped", () => setIsAwaitingResponse(false)),
    ];
    return () => {
      eventHandlers.forEach((unsubscribe) => unsubscribe());
    };
  }, [call]);

  const toggleRecording = useCallback(async () => {
    try {
      setIsAwaitingResponse(true);
      if (isCallRecordingInProgress) {
        await call?.stopRecording();
      } else {
        await call?.startRecording();
      }
    } catch (e) {
      console.error(`Failed start recording`, e);
    }
  }, [call, isCallRecordingInProgress]);

  return isAwaitingResponse ? (
    <LoadingIndicator />
  ) : (
    <button onClick={toggleRecording}>{/* Button content */}</button>
  );
};
```

### Permissions

To start and stop recording, the user has to have corresponding permissions. We therefore encourage the integrators to check the permissions before allowing users to execute a given action. The SDK bundles component [`Restricted`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-bindings/src/wrappers/Restricted.tsx) that renders its children only if the user has sufficient permissions. The SDK's [`RecordCallButton`](https://getstream.io/video/docs/react/v2/ui-components/call/call-controls/#recordcallbutton) makes use of the [`Restricted`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-bindings/src/wrappers/Restricted.tsx) component.

To learn more about permissions, take a look at our [permissions guide](https://getstream.io/video/docs/react/v2/guides/permissions-and-moderation/).

### Call recording SDK components

The React SDK comes with [`RecordCallButton`](https://getstream.io/video/docs/react/v2/ui-components/call/call-controls/#recordcallbutton), that takes care of initiating the call recording.

## Acquiring call recordings data

The call recording data can be retrieved by calling the `Call` method `listRecordings()`. By default, it retrieves all the call recordings for a given call (call CID). However, it accepts a single argument `callSessionId` that allows us to retrieve only recordings done during a single call session. The method returns `ListRecordingsResponse` that carries an array of `CallRecording` objects accessible through `recordings` key.

```tsx
import { useEffect, useState } from "react";
import { CallRecording, useCall } from "@stream-io/video-react-sdk";

export const RecordingsList = () => {
  const call = useCall();
  const [recordings, setRecordings] = useState<CallRecording[]>([]);

  useEffect(() => {
    if (!call) return;

    const loadRecordings = async () => {
      // callSessionId is optional:
      // - no argument => recordings across all sessions for this call CID
      const allRecordings = await call.listRecordings();
      setRecordings(allRecordings.recordings);

      // - with callSessionId => recordings for a specific session only
      const callSessionId = call.state.session?.id;
      if (callSessionId) {
        const currentSessionRecordings =
          await call.listRecordings(callSessionId);
        console.log(currentSessionRecordings.recordings);
      }
    };

    loadRecordings();
  }, [call]);

  return <>{recordings.length} recordings found</>;
};
```

>
> **Note:** Use the `listRecordings()` API for listing data. `queryRecordings()` is deprecated.
>

>
> **Note:** Multiple calls can be recorded during a single call session, but a single call CID can be reused for multiple sessions, too.
>

>
> **Note:** The call recording is not immediately available when the `call.recording_stopped` event is delivered.
> It may take 30 or more seconds for a recording to be available, advertised by emitting `call.recording_ready` event.
>

## Call recording listing SDK components

The React SDK provides [`CallRecordingList`](https://getstream.io/video/docs/react/v2/ui-components/call/call-recordings-list/) component to display call recordings.

## Storage and Retention

By default, recordings are stored in an AWS S3 bucket managed by Stream. Files are retained for 2 weeks before being automatically deleted.

| Setting          | Default Value                                           |
| ---------------- | ------------------------------------------------------- |
| Storage          | Stream-managed CDN                                      |
| Retention period | 2 weeks                                                 |
| Download URLs    | Signed, expire after 2 weeks                            |
| Upload timing    | Immediate. Recording length affects the upload duration |

### External Storage

You can configure your own storage solution (S3-compatible) to keep recordings longer or store them outside Stream.

See the [Recording Storage](https://getstream.io/video/docs/api/recording/storage/) documentation for configuration details.

## Troubleshooting

### Recording not starting

If `call.startRecording()` has no effect:

1. Verify the user has recording permission for the call type in the Dashboard
2. Listen for `call.recording_started` event or use `useIsCallRecordingInProgress` hook
3. Check that recording is enabled for the call type in Dashboard settings
4. Note the call ID for support if the issue persists

### Blank recordings

If recordings are created but contain no audio/video:

1. Verify participants joined with audio/video enabled
2. Check the logs in our dashboard for `JoinCall failed` errors (internal recording service permissions)
3. Review recording layout configuration

See the [API Recording Documentation](https://getstream.io/video/docs/api/recording/composite/) for more details.

---

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react/v2/advanced/recording/](https://getstream.io/video/docs/react/v2/advanced/recording/).