SignalWire APIs support recording the audio and video of a room session. We are going to implement recording in our video-conferencing app.


First, we need to update our server to allow clients to obtain a recording. Clients cannot directly access the mp4 file of a recording, so we use our server like a proxy. Let's go back to the backend that we built in Writing a backend to proxy SignalWire Video REST API, and let's add the following endpoint:

app.get("/get_recording/:id", async (req, res) => {
  try {
    const rec = await axios.get(`${apiurl}/room_recordings/${}`, { auth })
  } catch (e) {
    return res.sendStatus(500);

When a client performs a GET request to our endpoint, specifying the id of a recording in the path, it gets back a recording object which includes the url of the .mp4 file. In real settings, please make sure to validate your inputs (

We are also going to add "room.recording" to the list of moderatorPermissions, so that moderators are able to control recordings:

const moderatorPermissions = [



On the frontend side, let's add a new file called components/RecordingButton.js. This will be a toggle button that, when pressed, can either start or stop a recording. After a recording is stopped, as soon as it is ready its associated object (the one coming from our backend, which includes the .mp4 url) is sent as a parameter of the recordingReady callback.

import React, { useState } from "react";
import ToggleButton from "react-bootstrap/ToggleButton";
import axios from "axios";

export default function RecordingButton({ room, eventLogger, recordingReady }) {
  let [recordingObj, setRecordingObj] = useState();
  return (
      onClick={async (e) => {
        if (!recordingObj) {
          const rec = await room.startRecording()
        } else {
          const recId =
          await recordingObj.stop()

          eventLogger(`Your recording is being processed and will be downloaded shortly.`)

          // Get the recording
          // Give the server a bit of time to process the file
          await retry(async () => {
            const res = await axios.get(`/get_recording/${recId}`)
            if ( && {
              return true
            return false
          }, 1000, 5)

async function retry(fn, timeout_ms, retries) {
  if (retries > 0 && !await fn()) {
    await new Promise(resolve => setTimeout(resolve, timeout_ms));
    retry(fn, timeout_ms, retries - 1);

As you can observe from the code, a recording is started by calling room.startRecording(), which returns a recording object that can be used later to stop that same recording.


Within InCall.js we need to display the recording button. First we import it:

import RecordingButton from "../components/RecordingButton";

Then we just add it to the DOM:

  recordingReady={(rec) => {
    const link = document.createElement('a');
    link.href = rec.uri;
    link.setAttribute('download', true);

when recordingReady is called, we get the uri of the .mp4 file and we trigger a download.

You can find the complete application on CodeSandbox, and on GitHub in the branch "extras":

Did this page help you?