Skip to main content


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":