← Back to the blog
Guides #recording

Record a room to S3 and get a VOD

Start a recording with one call, let egress and the upload job do the rest, and end up with a playable VOD in your own bucket. Including split parts and snapshots.

2 min read
Leer en español

From live room to VOD in three moves

Recording in StreamHub is a fire-and-forget operation: you start it, and a background pipeline turns the live room into an uploaded, indexed VOD in your app's S3 bucket.

Make sure the app has S3 configured first (see the per-app S3 guide), then:

Start the recording

curl -s -X POST $BASE/apps/live/recording/start \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"roomName":"studio"}'

You get back a recording handle:

{ "data": { "vodId": 12, "egressId": "EG_xxx", "status": "recording" } }

Behind the scenes LiveKit egress starts writing a local MP4 and a VOD row is inserted with status=recording. The recording_started webhook fires.

Let the pipeline finish it

You do not poll or babysit. When egress ends:

  1. the file's status moves to uploading and an upload job is enqueued,
  2. the job uploads to the app's S3, builds a public/presigned URL, generates a snapshot, and deletes the local file,
  3. status becomes ready, metadata (duration, resolution, codec) is saved, and vod_ready fires.

If the upload fails, the local file is kept and recording_failed fires — nothing is lost silently.

Recording an already-live stream

If a stream is already publishing, record it in place by stream id:

curl -s -X POST $BASE/apps/live/streams/<id>/record/start \
  -H "Authorization: Bearer $TOKEN"

Stop either kind with recording/:id/stop.

Split parts and snapshots

The output shape comes from the app config, not the request:

  • split_minutes > 0 cuts the recording every N minutes — each part becomes its own VOD, perfect for 24/7 cameras. Allowed values include 15, 30, 60, 90, 120.
  • snapshot_seconds > 0 grabs a periodic JPEG thumbnail during the recording.

You can also grab a one-off frame any time:

curl -s -X POST $BASE/apps/live/snapshots \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"room":"studio"}'

Watch the VOD

Once status=ready, the VOD has a playable URL (presigned or public depending on your config), served by the video.js VOD player. Because recordings honor max_recording_minutes_month, you can cap spend per tenant while still recording everything that matters.