Recording

One highly sought-after feature in many communication applications is call recording. Whether it’s for legal compliance, quality assurance, or simply for future reference, the ability to record calls is essential in numerous scenarios.

This documentation article serves as a guide for implementing a call recording feature in your application. We will explore the technical aspects and best practices involved in capturing and storing recording data during calls.

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.

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 that renders its children only if the user has sufficient permissions. The SDK’s RecordCallButton makes use of the Restricted component.

To learn more about permissions, take a look at our permissions guide.

Call recording SDK components

The React SDK comes with 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 queryRecordings(). 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.

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

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

SettingDefault Value
StorageStream-managed CDN
Retention period2 weeks
Download URLsSigned, expire after 2 weeks
Upload timingImmediate. 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 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 for more details.