Cloud recording allows apps to save voice calls, video calls, and live streams so users can revisit important conversations at any time. This improves user experience by helping them catch missed details or review key moments. While the idea sounds simple, implementing recording features involves complex systems for capturing media, securely storing files, and providing easy access. Building this from scratch can be time-consuming and costly. In this article, we’ll show you how to integrate cloud recording into your app using ZEGOCLOUD’s Cloud Recording API, including how to start recordings, save files automatically, and help users easily access their content.
How to Add Cloud Recording into Your App
Now let’s add cloud recording to your app using ZEGOCLOUD. The ZEGOCLOUD’s Cloud Recording API lets you add recording to existing video call and streaming apps with just a few server requests. If you already have a video or voice call app working, recording integration takes minutes instead of months.
We’ll use server-side API calls to start recordings during important calls, monitor their progress automatically, and give users access to saved files through your chosen cloud storage. No additional SDK integration required.
Prerequisites
Before you start building, make sure you have these things ready:
- Working video or voice calling app using ZEGOCLOUD SDK.
- Cloud recording service enabled.
- Third-party cloud storage account (AWS S3, Alibaba Cloud, etc.)
- Server-side environment to make API calls.
AppID
andServerSecret
from ZEGOCLOUD Console.
1. Set Up API Authentication
Cloud recording uses server-side APIs that require proper authentication. Set up signature generation to secure your API calls.
const crypto = require('crypto');
class ZegoCloudAPI {
constructor(appId, serverSecret) {
this.appId = appId;
this.serverSecret = serverSecret;
this.baseURL = 'https://cloudrecord-api.zego.im/';
}
generateSignature(timestamp, nonce) {
// Signature = md5(AppId + SignatureNonce + ServerSecret + Timestamp)
const data = `${this.appId}${nonce}${this.serverSecret}${timestamp}`;
return crypto.createHash('md5').update(data).digest('hex');
}
generateNonce() {
return crypto.randomBytes(8).toString('hex');
}
buildCommonParams() {
const timestamp = Math.floor(Date.now() / 1000);
const nonce = this.generateNonce();
const signature = this.generateSignature(timestamp, nonce);
return {
AppId: this.appId,
Timestamp: timestamp,
SignatureNonce: nonce,
Signature: signature,
SignatureVersion: '2.0'
};
}
}
2. Configure Cloud Storage
Set up your preferred cloud storage provider where recording files will be saved automatically. Choose your storage provider based on geographic location, pricing, and integration preferences.
const createStorageConfig = (provider, credentials) => {
const storageConfigs = {
// AWS S3 Configuration
s3: {
Vendor: 1,
Region: credentials.region,
Bucket: credentials.bucket,
AccessKeyId: credentials.accessKeyId,
AccessKeySecret: credentials.accessKeySecret
},
// Alibaba Cloud OSS Configuration
alibabaOSS: {
Vendor: 2,
Region: credentials.region,
Bucket: credentials.bucket,
AccessKeyId: credentials.accessKeyId,
AccessKeySecret: credentials.accessKeySecret
},
// Tencent Cloud COS Configuration
tencentCOS: {
Vendor: 3,
Region: credentials.region,
Bucket: credentials.bucket,
AccessKeyId: credentials.accessKeyId,
AccessKeySecret: credentials.accessKeySecret
}
};
return storageConfigs[provider];
};
3. Start Recording Sessions
Begin recording by calling the StartRecord
API when users start important conversations or meetings.
const startRecording = async (roomId, recordingOptions = {}) => {
const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
const commonParams = api.buildCommonParams();
// Build recording configuration
const recordingConfig = {
RoomId: roomId,
RecordInputParams: {
RecordMode: recordingOptions.mode || 2, // 1=single-stream, 2=mixed-stream
StreamType: 3, // 1=audio only, 2=video only, 3=audio+video
MaxIdleTime: 300, // Stop after 5 minutes of no streams
MaxRecordTime: 86400, // Maximum 24 hours
MixConfig: {
MixMode: 2, // 1=custom, 2=grid, 3=horizontal, 4=vertical, 5=floating
MixOutputStreamId: `mix_${roomId}`,
MixOutputVideoConfig: {
Width: 1280,
Height: 720,
Fps: 15,
Bitrate: 1130000
}
}
},
RecordOutputParams: {
OutputFileFormat: 'mp4',
OutputFolder: `recordings/${new Date().getFullYear()}/`
},
StorageParams: createStorageConfig('s3', {
region: 'us-west-2',
bucket: 'your-recordings-bucket',
accessKeyId: process.env.AWS_ACCESS_KEY,
accessKeySecret: process.env.AWS_SECRET_KEY
})
};
// Make API request
const queryParams = new URLSearchParams({
Action: 'StartRecord',
...commonParams
});
try {
const response = await fetch(`${api.baseURL}?${queryParams}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(recordingConfig)
});
const result = await response.json();
if (result.Code === 0) {
console.log('Recording started:', result.Data.TaskId);
return result.Data.TaskId;
} else {
throw new Error(`Recording failed: ${result.Message}`);
}
} catch (error) {
console.error('Start recording error:', error);
throw error;
}
};
This function creates a recording task and returns a TaskId that you’ll use for all subsequent operations.
4. Monitor Recording Status
Check recording progress and file upload status using the DescribeRecordStatus
API. This monitoring system tracks recording progress and notifies your app when files become available.
const checkRecordingStatus = async (taskId) => {
const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
const commonParams = api.buildCommonParams();
const queryParams = new URLSearchParams({
Action: 'DescribeRecordStatus',
...commonParams
});
try {
const response = await fetch(`${api.baseURL}?${queryParams}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ TaskId: taskId })
});
const result = await response.json();
if (result.Code === 0) {
return {
status: result.Data.Status, // 1=initialized, 2=ongoing, 3=completed, 4=failed, 5=paused
startTime: result.Data.RecordBeginTimestamp,
endTime: result.Data.RecordEndTimestamp,
files: result.Data.RecordFiles || []
};
} else {
throw new Error(`Status check failed: ${result.Message}`);
}
} catch (error) {
console.error('Status check error:', error);
throw error;
}
};
const monitorRecording = async (taskId, onStatusChange) => {
const checkInterval = setInterval(async () => {
try {
const status = await checkRecordingStatus(taskId);
if (onStatusChange) {
onStatusChange(status);
}
// Stop monitoring when recording completes or fails
if (status.status === 3 || status.status === 4) {
clearInterval(checkInterval);
console.log('Recording monitoring ended:', status);
}
} catch (error) {
console.error('Monitoring error:', error);
}
}, 10000); // Check every 10 seconds
return checkInterval;
};
5. Update Recording Layout
Change how participants appear in mixed recordings during active sessions.
const updateRecordingLayout = async (taskId, layoutType, customLayout = null) => {
const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
const commonParams = api.buildCommonParams();
const layoutConfig = {
TaskId: taskId,
MixMode: layoutType // 1=custom, 2=grid, 3=horizontal, 4=vertical, 5=floating
};
// Add custom layout configuration if provided
if (layoutType === 1 && customLayout) {
layoutConfig.MixInputList = customLayout.map(region => ({
StreamId: region.streamId || '',
ViewType: 1, // 1=audio/video, 2=whiteboard
Top: region.top,
Left: region.left,
Bottom: region.bottom,
Right: region.right,
Layer: region.layer || 1
}));
}
const queryParams = new URLSearchParams({
Action: 'UpdateLayout',
...commonParams
});
try {
const response = await fetch(`${api.baseURL}?${queryParams}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(layoutConfig)
});
const result = await response.json();
if (result.Code === 0) {
console.log('Layout updated successfully');
return true;
} else {
throw new Error(`Layout update failed: ${result.Message}`);
}
} catch (error) {
console.error('Layout update error:', error);
throw error;
}
};
// Example usage: Switch to grid layout
await updateRecordingLayout(taskId, 2);
// Example usage: Custom layout with two participants
await updateRecordingLayout(taskId, 1, [
{ streamId: 'user_123', top: 0, left: 0, bottom: 720, right: 640, layer: 1 },
{ streamId: 'user_456', top: 0, left: 640, bottom: 360, right: 1280, layer: 1 }
]);
Layout updates happen immediately without interrupting the recording process.
6. Stop Recording Sessions
End recording sessions when calls finish or reach natural stopping points.
const stopRecording = async (taskId) => {
const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
const commonParams = api.buildCommonParams();
const queryParams = new URLSearchParams({
Action: 'StopRecord',
...commonParams
});
try {
const response = await fetch(`${api.baseURL}?${queryParams}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ TaskId: taskId })
});
const result = await response.json();
if (result.Code === 0) {
console.log('Recording stopped successfully');
// Continue monitoring for file upload completion
return monitorRecording(taskId, (status) => {
if (status.status === 3) {
console.log('Recording files ready:', status.files);
}
});
} else {
throw new Error(`Stop recording failed: ${result.Message}`);
}
} catch (error) {
console.error('Stop recording error:', error);
throw error;
}
};
Always stop recordings explicitly to prevent unnecessary charges and ensure proper file processing.
7. Handle Recording Events
Set up webhook endpoints to receive real-time notifications about recording status changes.
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const verifyWebhookSignature = (secret, timestamp, nonce, signature) => {
const data = [secret, timestamp, nonce].sort().join('');
const computed = crypto.createHash('sha1').update(data).digest('hex');
return computed === signature;
};
app.post('/webhooks/recording', (req, res) => {
const {
signature,
timestamp,
nonce,
app_id,
task_id,
room_id,
event_type,
detail
} = req.body;
// Verify webhook authenticity
if (!verifyWebhookSignature(CALLBACK_SECRET, timestamp, nonce, signature)) {
return res.status(401).send('Invalid signature');
}
console.log(`Recording event: ${event_type} for task ${task_id}`);
switch (event_type) {
case 1: // Recording file upload completed
console.log('Files uploaded:', detail.file_info);
notifyUsersRecordingReady(task_id, detail.file_info);
break;
case 2: // Recording ended abnormally
console.error('Recording failed:', detail);
handleRecordingFailure(task_id, detail);
break;
case 5: // Recording completed normally
console.log('Recording completed successfully');
break;
case 7: // File upload in progress
console.log('Uploading files...');
break;
default:
console.log('Unknown event type:', event_type);
}
res.status(200).send('OK');
});
const notifyUsersRecordingReady = (taskId, fileList) => {
// Implement user notification logic
fileList.forEach(file => {
console.log(`File ready: ${file.file_id}, Size: ${file.file_size} bytes`);
});
};
8. Query Recording History
Retrieve lists of past and ongoing recording sessions for management and user access.
const getRecordingHistory = async (options = {}) => {
const api = new ZegoCloudAPI(APP_ID, SERVER_SECRET);
const commonParams = api.buildCommonParams();
const queryParams = new URLSearchParams({
Action: 'DescribeTasks',
...commonParams
});
const requestBody = {
Status: options.status || 3, // 2=ongoing, 3=completed
StartTime: options.startTime || (Date.now() - 3 * 24 * 60 * 60 * 1000), // Last 3 days
EndTime: options.endTime || Date.now(),
PageOffset: options.page || 1,
PageSize: options.pageSize || 50
};
if (options.roomId) {
requestBody.RoomId = options.roomId;
}
try {
const response = await fetch(`${api.baseURL}?${queryParams}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
});
const result = await response.json();
if (result.Code === 0) {
return {
total: result.Data.TotalCount,
page: result.Data.PageOffset,
pageSize: result.Data.PageSize,
recordings: result.Data.TaskList
};
} else {
throw new Error(`Query failed: ${result.Message}`);
}
} catch (error) {
console.error('History query error:', error);
throw error;
}
};
// Example: Get completed recordings from last week
const lastWeek = await getRecordingHistory({
status: 3,
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000,
endTime: Date.now()
});
Conclusion
Your app now has reliable cloud recording powered by ZEGOCLOUD’s server APIs. Users can capture important conversations automatically without worrying about device storage or missing crucial details during calls.
ZEGOCLOUD’s infrastructure handles all the complex recording tasks – video processing, file compression, and cloud storage uploads happen seamlessly in the background. Your app just makes simple API calls to start and stop recordings.
This recording foundation supports many use cases. Customer support teams can review service calls. Remote teams can catch up on missed meetings. Educational platforms can provide lecture replays. The recording files work with any video player, making them easy to share and access.
FAQ
Q1: What is cloud recording in the context of real-time apps?
This refers to the ability to automatically record audio, video, or screen content from a live session (like a video call or livestream) and store it on a cloud server. It allows for on-demand playback, compliance, or content reuse without requiring users to save files locally.
Q2: How does cloud recording work in a video/audio SDK?
Most SDKs offer cloud recording by routing media streams through a cloud server, where they are captured and saved. Developers usually trigger recording sessions via API calls and can configure formats, layouts, storage locations, and duration.
Q3: Does cloud recording affect app performance or latency?
No, it typically runs on the server side and does not impact user-side performance. The recording server handles all processing, so users can continue using the app without noticing any delay or quality drop.
Let’s Build APP Together
Start building with real-time video, voice & chat SDK for apps today!