big-school-62939
04/06/2026, 10:02 PMtall-airline-16756
04/06/2026, 10:03 PMtall-airline-16756
04/06/2026, 10:03 PMlk.transcription text stream and via agent transcription nodes) and provide timing/sync options. Agent transcripts and observability data can be used alongside Egress recordings for synchronized playback/analysis.
Would you like help with:
• configuring an Agent to produce transcripts for a room, or
• wiring Egress recordings and Agent transcription output together (storage, timing, or formats)? Sources: Universal Egress | LiveKit | Text and transcriptions | LiveKit Documentation | Understanding Transcript Timing in Voice Conversations | LiveKit Agent Observability | Debug and optimize voice agents | LiveKit, with interactive elementsbig-school-62939
04/06/2026, 10:07 PMtall-airline-16756
04/06/2026, 10:08 PMegress_ended webhook (it contains the output file URL).
c. In a Cloud Function (or your backend) triggered by the webhook:
• Read the file from GCS (or run STT if you need transcripts).
• Save structured data to Firestore (file URL, metadata, transcript, timestamps, session IDs).
• Alternatives:
◦ Export to S3 and use an AWS Lambda to write metadata to Firestore.
◦ Stream audio via WebSocket for real‑time processing, then persist results to Firestore.
Would you like a sample Cloud Function (GCS -> Firestore) or the exact webhook payload fields to use?
Sources: Universal Egress | LiveKit | Best practices for managing webhook event streams | LiveKit | server-sdk-js | livekit/livekit_composite | DeepWiki, with interactive elementsbig-school-62939
04/06/2026, 10:14 PMtall-airline-16756
04/06/2026, 10:14 PMegress_ended webhook:
◦ Subscribe to the webhook so you receive the output file URL and metadata when recording finishes.
1. (Optional) Move/serve via Firebase Storage SDK:
◦ If you want the file surfaced through Firebase Storage APIs, keep the file in the project’s GCS bucket (no extra copy needed). Firebase Storage clients can access files in that bucket, subject to Firebase security rules and IAM.
1. (Optional) Post-processing:
◦ Trigger a Cloud Function from the egress_ended webhook or a GCS upload trigger to update Firestore, generate thumbnails/transcripts, change ACLs, or notify users.
If you’d like, I can provide:
• A sample Egress output config targeting GCS,
• Example IAM bindings for the service account, or
• A Cloud Function template that handles egress_ended and updates Firebase (Storage/Firestore). Which would help most?, with interactive elementsbig-school-62939
04/06/2026, 11:04 PMtall-airline-16756
04/06/2026, 11:05 PMegress_ended and save metadata to Firestore.
Prerequisites:
• livekit-server-sdk installed
• A GCS service account JSON (client_email and private_key)
• LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_URL, GCS_CLIENT_EMAIL, GCS_PRIVATE_KEY, GCS_BUCKET env vars
• Your GCS bucket belongs to the Firebase project if you want Firebase Storage access
1) Start Egress to GCS (server-side)
// startEgress.js
const { EgressClient, RoomCompositeEgressRequest } = require('livekit-server-sdk');
const egressClient = new EgressClient({
apiKey: process.env.LIVEKIT_API_KEY,
apiSecret: process.env.LIVEKIT_API_SECRET,
host: process.env.LIVEKIT_URL,
});
async function startEgressToGCS(roomName, gcsPath) {
const req = new RoomCompositeEgressRequest({
roomName,
output: {
file: {
filepath: gcsPath, // e.g., 'recordings/session-123.mp4'
gcs: {
bucket: process.env.GCS_BUCKET,
credentials: {
client_email: process.env.GCS_CLIENT_EMAIL,
private_key: process.env.GCS_PRIVATE_KEY,
},
},
},
},
// optional: format/layout settings
// layout: 'speaker-dark',
});
const info = await egressClient.startRoomCompositeEgress(req);
console.log('egress started:', info.egressId);
return info;
}
module.exports = { startEgressToGCS };
2) Webhook to receive egress completion and write metadata to Firestore
// webhook.js
const express = require('express');
const bodyParser = require('body-parser');
const admin = require('firebase-admin');
const { WebhookReceiver } = require('livekit-server-sdk');
admin.initializeApp(); // make sure FIREBASE credentials are set in env or runtime
const db = admin.firestore();
const receiver = new WebhookReceiver(process.env.LIVEKIT_API_KEY, process.env.LIVEKIT_API_SECRET);
const app = express();
app.post('/webhook', bodyParser.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = receiver.receive(req.body, req.headers['authorization']);
} catch (err) {
console.error('invalid webhook signature', err);
return res.status(400).end();
}
if (event.event === 'egress_ended' && event.egressInfo?.status === 'EGRESS_COMPLETE') {
const info = event.egressInfo;
const fileUrl = info.file?.location || null;
const doc = {
egressId: info.egressId,
room: info.roomName,
status: info.status,
fileUrl,
startedAt: info.startedAt,
endedAt: info.endedAt,
// add any other fields you need
};
try {
await db.collection('egressRecords').doc(info.egressId).set(doc);
console.log('egress metadata written to Firestore:', info.egressId);
} catch (err) {
console.error('failed to write to Firestore', err);
}
}
res.status(200).end();
});
module.exports = app;
Notes and tips:
• The GCS credentials in the Egress request are the service account that LiveKit will use to upload the file; ensure the service account has storage.objectCreator/storage.objectAdmin on the bucket.
• If you prefer, you can export to a bucket you control and set Firebase security rules/IAM to allow access from Firebase clients.
• Use the egress_ended webhook payload egressInfo.file.location for the stored object URL; you can then surface that URL via Firebase Storage APIs or Firestore documents.
Would you like a ready-to-run example repository or the exact webhook payload shape copied for your function?
Sources: Universal Egress | LiveKit | server-sdk-js | livekit/livekit_composite | DeepWiki, with interactive elements