LevelChat records lossless during the call, then runs a SVT-AV1 two-pass compression worker on egress. You pay for hot storage of compressed artifacts only — raw masters age out fast.
Start a recording
TypeScript
const rec = await room.record({
compose: 'tracks', // "tracks" | "grid" | "speaker"
include_audio: true,
include_video: true,
include_screen: true,
participants: ['user_42', 'user_7'], // omit for "everyone"
format: {
output: 'mp4_av1', // mp4_av1 | mp4_h264 | hls | raw_mkv
also_generate_hls: true,
},
webhook: 'https://your-app.example/lc-rec',
});Compose modes
- tracks — one file per participant, per kind. Best for post-production editing.
- grid — composed evenly-tiled video. Best for archive playback.
- speaker — active-speaker switcher. Best for talks / podcasts.
Lifecycle
text
recording → raw_ready → ready → failed?raw_readyfires when the lossless MKV is closed and uploaded.readyfires when the transcoder finishes compression + (optional) HLS variants.failedalways carries arecording.failedwebhook with aProblem Detailspayload.
Listing & downloading
TypeScript
const page = await lc.recordings.list({ project_id, state: 'ready', limit: 50 });
for (const r of page.items) {
for (const a of r.artifacts) {
console.log(a.kind, a.url); // presigned, 10-min TTL by default
}
}Set public_recordings: true on the project if you want non-presigned URLs.
Retention
TypeScript
config: { record: { enabled: true, retention_days: 30 } }After retention_days from ended_at, every artifact is deleted. The metadata row in
recordings_index is kept indefinitely for accounting (with state = expired).