kind-iron-94532
08/06/2025, 5:37 AMtall-airline-16756
08/06/2025, 5:37 AMtall-airline-16756
08/06/2025, 5:38 AMroom_config.egress
settings to enable automatic recording for SIP calls.
2. Record Agent Sessions: Start a room composite recorder in your agent's entrypoint. This captures all participants and interactions in the room. Example code snippet: req = api.RoomCompositeEgressRequest(
room_name="my-room",
layout="speaker",
preset=api.EncodingOptionsPreset.H264_720P_30,
audio_only=False,
segment_outputs=[api.SegmentedFileOutput(
filename_prefix="my-output",
playlist_name="my-playlist.m3u8",
live_playlist_name="my-live-playlist.m3u8",
segment_duration=5,
gcp=api.GCPUpload(
credentials=file_contents,
bucket="<my-bucket>",
),
)],
)
3. Cloud Storage Configuration: Set up access to a cloud storage provider (e.g., Google Cloud Storage, Amazon S3) to upload the recordings.
4. Recording Process: Use the Egress.StartRoomCompositeEgress()
API to start and stop recordings based on participant activity.
Let me know if you need further details or assistance with any specific step!
Sources: SIP Integration | livekit/livekit_composite | DeepWiki | Speech & Audio Processing | livekit/livekit_composite | DeepWiki | Recording agent sessions | LiveKit Docs | Session recording and transcripts | LiveKit Docs | Custom recording templates | LiveKit Docstall-airline-16756
08/06/2025, 6:11 AMfrom livekit import api
2. Update the Entrypoint: Next, add the Egress setup to your entrypoint function. Here's an example: async def entrypoint(ctx: JobContext):
# Read credentials from file
file_contents = ""
with open("/path/to/credentials.json", "r") as f:
file_contents = f.read()
# Configure Egress
req = api.RoomCompositeEgressRequest(
room_name="my-room",
layout="speaker",
preset=api.EncodingOptionsPreset.H264_720P_30,
audio_only=False,
segment_outputs=[api.SegmentedFileOutput(
filename_prefix="my-output",
playlist_name="my-playlist.m3u8",
live_playlist_name="my-live-playlist.m3u8",
segment_duration=5,
gcp=api.GCPUpload(
credentials=file_contents,
bucket="<my-bucket>",
),
)],
)
lkapi = api.LiveKitAPI()
res = await lkapi.egress.start_room_composite_egress(req)
3. Cloud Storage Configuration: Make sure your cloud storage credentials (like for Google Cloud Storage or Amazon S3) are all set and valid.
4. Optional Audio-Only Recording: If you just want to record audio, simply set audio_only=True
in your Egress request.
If you hit any bumps or want help integrating this into your existing agent code, just let me know—I’m happy to help!kind-iron-94532
08/06/2025, 6:13 AMtall-airline-16756
08/06/2025, 6:13 AMkind-iron-94532
08/06/2025, 6:38 AMreq = api.RoomCompositeEgressRequest(
room_name="my-room",
layout="speaker",
preset=api.EncodingOptionsPreset.H264_720P_30,
audio_only=False,
segment_outputs=[api.SegmentedFileOutput(
filename_prefix="my-output",
playlist_name="my-playlist.m3u8",
live_playlist_name="my-live-playlist.m3u8",
segment_duration=5,
gcp=api.GCPUpload(
credentials=file_contents,
bucket="<my-bucket>",
),
)],
)
tall-airline-16756
08/06/2025, 6:39 AMRoomCompositeEgressRequest
to use the S3-compatible storage option. Here’s a handy refactor of the code for you:
Sources: protobufs/livekit_egress.proto | README.md | livekit-protocol/livekit/protocol/egress.pytall-airline-16756
08/06/2025, 7:16 AMRoomCompositeEgressRequest
with Supabase S3 looks spot on. Let me quickly walk you through the key parts to make sure everything’s clear:
• S3 Configuration: You’ve got all the important fields (access_key
, secret
, region
, endpoint
, and bucket
) set up just right.
• RoomCompositeEgressRequest: The essentials like room_name
, layout
, and segment_outputs
are all configured properly.
• Audio-Only Recording: Using audio_only=True
is perfect if you want to record just the audio.
If you have any questions, want to tweak something, or add optional stuff like metadata or tagging, just give me a shout—I’m here to help!
Sources: protobufs/livekit_egress.proto | livekit-protocol/livekit/protocol/egress.pykind-iron-94532
08/06/2025, 7:19 AMtall-airline-16756
08/06/2025, 7:20 AMfilename_prefix
and the way you set up the SegmentedFileOutput
. Here’s the scoop:
• Filename Prefix: The filename_prefix
you choose (like recording_file_name_prefix
) becomes the base name for your files.
• Segmented Files: Since you’re using segmented outputs, your recordings will be split into multiple files with names in order, such as recording_file_name_prefix_00001.ts
, recording_file_name_prefix_00002.ts
, and so forth.
• Directory Structure: If your prefix includes a folder path (for example, recordings/my-room
), the files will be saved right there in that folder inside your S3 bucket.
If you’d like to get creative and customize the naming even more—maybe add the room name or timestamps—just give me a shout! I’m happy to help you set that up.kind-iron-94532
08/06/2025, 7:20 AMtall-airline-16756
08/06/2025, 7:21 AMRoomCompositeEgressRequest
, the next step is to send it over to the LiveKit egress service to kick off the recording. Just having the request object sets things up, but it won’t start recording by itself.
Here’s a simple way to get the egress going:
lkapi = api.LiveKitAPI()
res = await lkapi.egress.start_room_composite_egress(rec_req)
await lkapi.aclose()
This will begin recording based on what you’ve configured. You’ll get back an egress_id
in the response, which is handy for keeping track of or managing your recording session.
If you have any questions or want a hand with this, just let me know—I’m here to help!
Sources: Session recording and transcripts | LiveKit Docs | Universal Egress | protobufs/livekit_egress.protokind-iron-94532
08/06/2025, 7:26 AMtall-airline-16756
08/06/2025, 7:26 AMstart_room_composite_egress
method to kick things off. This will give you an EgressInfo
object.
2. Grab the Recording URL: From that EgressInfo
, pull out the egress_id
and build the recording URL using your output settings. For example: recording_url = f"https://{req.segment_outputs[0].s3.bucket}.s3.{req.segment_outputs[0].s3.region}.<http://amazonaws.com/{req.segment_outputs[0].filename_prefix}|amazonaws.com/{req.segment_outputs[0].filename_prefix}>"
3. Save It in Your Database: Pop the egress_id
and recording_url
into your database so you can easily find them later. Like this: save_recording_info(egress_id, recording_url)
Just a heads-up: make sure to catch any errors along the way and double-check the URL before saving. If you want, I’m here to help with the database part!
Sources: Session recording and transcripts | LiveKit Docs | Egress API | LiveKit Docs | protobufs/livekit_egress.proto | cmd/lk/egress.gokind-iron-94532
08/06/2025, 11:15 AMtall-airline-16756
08/06/2025, 11:15 AMreq = api.RoomCompositeEgressRequest(
room_name="my-room",
audio_only=True, # For audio-only recordings
file_outputs=[api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG, # Specify OGG format
filepath="recordings/{room_name}-{time}.ogg",
s3=api.S3Upload(
bucket="your-bucket-name",
region="your-region",
access_key="your-access-key",
secret="your-secret-key",
),
)],
)
Just swap out the placeholders with your own details, and you’ll have your recording saved as an OGG file in your S3 bucket. If you have any questions or want me to walk you through it, just let me know—I’m here to help!kind-iron-94532
08/06/2025, 11:17 AMtall-airline-16756
08/06/2025, 11:17 AMRoomCompositeEgressRequest
with a local file path. Here's a quick example to help you out:
req = api.RoomCompositeEgressRequest(
room_name="my-room",
audio_only=True, # For audio-only recordings
file_outputs=[api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG, # Specify the file format
filepath="/path/to/recording.ogg", # Local file path
)],
)
Just swap out /path/to/recording.ogg
with wherever you'd like the file to be saved on your computer. Make sure that folder exists and that your app has permission to write there. If you run into any issues or have questions, feel free to ask—I'm here to help!kind-iron-94532
08/06/2025, 11:21 AM2025-08-06 16:50:06.644 | ERROR | agents.assistant_agent:on_enter:114 - Failed to start call recording egress: TwirpError(code=invalid_argument, message=request has missing or invalid field: output, status=400)
here is the code m using
recording_file_name = (
f"call-recording-{self.assistant.get('id')}-{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.ogg"
)
rec_req = api.RoomCompositeEgressRequest(
room_name=agents.get_job_context().room.name,
file_outputs=[
api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG,
filepath=recording_file_name,
)
],
audio_only=True,
# segment_outputs=[
# api.SegmentedFileOutput(
# filename_prefix=recording_file_name_prefix,
# segment_duration=5,
# s3=s3_upload_config,
# )
# ],
)
# Use a short-lived LiveKitAPI client so the underlying aiohttp.ClientSession
# is properly closed after the request finishes. Otherwise we leak the session
# and `asyncio` will complain with an "Unclosed client session" warning which
# we observed in the logs when starting call recording. Using an async context
# manager automatically invokes `aclose()` on exit and prevents the leak.
try:
async with api.LiveKitAPI() as lkapi:
info = await lkapi.egress.start_room_composite_egress(rec_req)
<http://logger.info|logger.info>(f"Started egress (id={info.egress_id}) for call recording")
except Exception as e:
logger.error(f"Failed to start call recording egress: {e}")
tall-airline-16756
08/06/2025, 11:22 AMrequest has missing or invalid field: output
pops up because the egress request is missing a properly set output destination. No worries though—here’s how you can fix it:
1. Make Sure Output is Specified: Every RoomCompositeEgressRequest
needs at least one valid output. Looks like you’re using file_outputs
, which is exactly right.
2. Double-Check the Output Setup: For EncodedFileOutput
, just confirm these:
◦ file_type
is set properly (like api.EncodedFileType.OGG
).
◦ filepath
points to a valid location (for example, recordings/my-room.ogg
).
1. Use the Right Field Format: You’re already on track using file_outputs
, which is perfect. Just avoid older fields like file
.
Here’s a tweaked version of your code to guide you:
recording_file_name = (
f"call-recording-{self.assistant.get('id')}-{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.ogg"
)
rec_req = api.RoomCompositeEgressRequest(
room_name=agents.get_job_context().room.name,
file_outputs=[
api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG,
filepath=f"recordings/{recording_file_name}",
)
],
audio_only=True,
)
Just a quick heads-up: make sure the recordings/
folder exists and you have permission to write to it. If you hit any more snags or want me to walk you through anything else, just give me a shout!
Sources: packages/livekit-server-sdk/src/EgressClient.ts | livekit-protocol/src/livekit.rs | livekit-protocol/livekit/protocol/egress.pyi | src/main/kotlin/io/livekit/server/EgressServiceClient.kt | pkg/errors/errors.gokind-iron-94532
08/06/2025, 11:33 AM2025-08-06 16:59:43.926 | INFO | __mp_main__:entrypoint:155 - Agent entrypoint finished.
2025-08-06 16:59:43,926 - DEBUG livekit.agents - using audio io: `RoomIO` -> `AgentSession` -> `TranscriptSynchronizer` -> `RoomIO` {"pid": 28825, "job_id": "AJ_MUCk5DEFBgUn"}
2025-08-06 16:59:43,926 - DEBUG livekit.agents - using transcript io: `AgentSession` -> `TranscriptSynchronizer` -> `RoomIO` {"pid": 28825, "job_id": "AJ_MUCk5DEFBgUn"}
2025-08-06 16:59:44.447 | ERROR | agents.assistant_agent:on_enter:118 - Failed to start call recording egress: TwirpError(code=invalid_argument, message=request has missing or invalid field: output, status=400)
tall-airline-16756
08/06/2025, 11:34 AMEncodedFileOutput
is missing a storage destination. Besides specifying the file_type
and filepath
, you’ll need to add at least one storage destination like S3, GCP, Azure, or AliOSS.
Here’s a quick example of how to save the file locally:
rec_req = api.RoomCompositeEgressRequest(
room_name=agents.get_job_context().room.name,
file_outputs=[
api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG,
filepath="/path/to/recordings/call-recording.ogg",
)
],
audio_only=True,
)
If you’re using cloud storage (like S3), you’ll want to include the storage details like this:
rec_req = api.RoomCompositeEgressRequest(
room_name=agents.get_job_context().room.name,
file_outputs=[
api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG,
filepath="recordings/call-recording.ogg",
s3=api.S3Upload(
bucket="your-bucket-name",
region="your-region",
access_key="your-access-key",
secret="your-secret-key",
),
)
],
audio_only=True,
)
Just double-check that the folder or storage bucket exists and you have permission to write to it. If you’re still running into trouble or want a hand with anything else, feel free to reach out—I’m here to help!
Sources: Egress API | LiveKit Docs | livekit/livekit_egress.pb.gokind-iron-94532
08/06/2025, 11:55 AMs3_upload_config = api.S3Upload(
access_key=settings.SUPABASE_S3_ACCESS_KEY.get_secret_value(),
secret=settings.SUPABASE_S3_SECRET_KEY.get_secret_value(),
region=settings.SUPABASE_S3_REGION.get_secret_value(),
endpoint=settings.SUPABASE_S3_STORAGE_URL.get_secret_value(),
bucket=settings.SUPABASE_S3_BUCKET_NAME.get_secret_value(),
)
recording_file_name = (
f"call-recording-{self.assistant.get('id')}-{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.ogg"
)
filepath = os.path.join(os.getcwd(), recording_file_name)
<http://logger.info|logger.info>(f"Recording file path: {filepath}")
rec_req = api.RoomCompositeEgressRequest(
room_name=agents.get_job_context().room.name,
layout="speaker",
file_outputs=[
api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG,
filepath=filepath,
s3=s3_upload_config,
)
],
audio_only=True,
)
try:
async with api.LiveKitAPI() as lkapi:
info = await lkapi.egress.start_room_composite_egress(rec_req)
<http://logger.info|logger.info>(f"Started egress (id={info.egress_id}) for call recording")
except Exception as e:
logger.error(f"Failed to start call recording egress: {e}")
tall-airline-16756
08/06/2025, 11:56 AMEncodedFileOutput
can’t handle both local file paths and S3 settings at the same time. Just so you know, the filepath
field is actually a filename template for where the file goes in your cloud storage (like S3), not a local file path.
Here’s a simple way to fix your configuration:
rec_req = api.RoomCompositeEgressRequest(
room_name=agents.get_job_context().room.name,
file_outputs=[
api.EncodedFileOutput(
file_type=api.EncodedFileType.OGG,
filepath="recordings/call-recording.ogg", # Path within S3 bucket
s3=api.S3Upload(
bucket=settings.SUPABASE_S3_BUCKET_NAME.get_secret_value(),
region=settings.SUPABASE_S3_REGION.get_secret_value(),
access_key=settings.SUPABASE_S3_ACCESS_KEY.get_secret_value(),
secret=settings.SUPABASE_S3_SECRET_KEY.get_secret_value(),
endpoint=settings.SUPABASE_S3_STORAGE_URL.get_secret_value(),
),
)
],
audio_only=True,
)
With this setup, your recording will be saved right to your S3 bucket at the path you specified. If you want to get the file locally afterward, you can just download it from S3 once the recording’s done. Just shout if you want a hand with that!
Sources: Egress API | LiveKit Docs | livekit/livekit_egress.pb.go | Session recording and transcripts | LiveKit Docs