LevelChatDocs
Docs
Recordings

Recordings

Record raw, ship compressed, store cheap.

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_ready fires when the lossless MKV is closed and uploaded.
  • ready fires when the transcoder finishes compression + (optional) HLS variants.
  • failed always carries a recording.failed webhook with a Problem Details payload.

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