fancy-florist-18763
02/24/2026, 6:21 PMtall-airline-16756
02/24/2026, 6:21 PMtall-airline-16756
02/24/2026, 6:21 PMRoomServiceClient (or the equivalent in your chosen SDK) to list participants and get room metadata — this gives you the core session details.
• For agent sessions with observability enabled, the LiveKit Cloud Dashboard exposes session recordings, traces, transcripts, and logs. Those observability artifacts can be viewed in the dashboard or downloaded as JSON.
To help further
• Which language or SDK are you using (Go, JS/TS, Kotlin, Ruby, etc.)?
• Do you need participant lists, room metadata, connection states, or the observability artifacts (recording/transcript/logs)?
• Are you looking for a server-side API call or a client-side method?
If you tell me your SDK/language and the exact session fields you need, I’ll give a short code example or the exact method to call.
Sources: livekit/livekit_composite | DeepWiki | Streamline troubleshooting with Agent Observability | Where to find your Agent Logs | LiveKitfancy-florist-18763
02/24/2026, 6:24 PMfancy-florist-18763
02/24/2026, 6:25 PMtall-airline-16756
02/24/2026, 6:25 PMpip install livekit-api
• Have your LiveKit URL, API key, and API secret available.
Example: async Python usage (recommended)
import asyncio
from livekit import api
async def fetch_session_details():
client = api.LiveKitAPI(
url="<https://your-livekit-instance.com>",
api_key="your-api-key",
api_secret="your-api-secret"
)
# List all active rooms (sessions)
rooms = await client.room.list_rooms()
for r in rooms:
print(f"Room: {r.name}, SID: {r.sid}, Num participants: {len(r.participants)}")
# Get a specific room by name
room_name = "my-room-name"
room_info = await client.room.get_room(room_name=room_name)
print("Room metadata:", room_info.metadata)
print("Room created at:", room_info.created_at)
# List participants in that room
participants = await client.room.list_participants(room_name=room_name)
for p in participants:
print(f"Participant: {p.identity}, State: {p.state}, SID: {p.sid}, Metadata: {p.metadata}")
# Get a specific participant
participant = await client.room.get_participant(room_name=room_name, identity="alice")
print("Participant tracks:", [t.track_sid for t in participant.tracks])
await client.aclose()
asyncio.run(fetch_session_details())
Synchronous alternative (if you prefer sync code)
• The SDK is async-first. If you must run sync, run the async calls inside an event loop (as shown) or use an async runner wrapper.
Which fields you can get
• Room properties: name, sid, metadata, created_at, participants list, etc.
• Participant properties: identity, sid, state, metadata, tracks, etc.
Questions to help me tailor this
• Do you want a minimal snippet for a single endpoint (e.g., get_room + list_participants) to plug into your server?
• Are you running inside a framework (FastAPI, Django) where I should show an endpoint example?
• Do you need observability artifacts (recordings, transcripts) instead of realtime session state?fancy-florist-18763
02/24/2026, 7:13 PMtall-airline-16756
02/24/2026, 7:14 PM@session.on("user_input_transcribed")
def on_transcript(transcript):
if transcript.is_final:
print("Final transcript:", transcript.transcript)
This gives you immediate access to final transcript turns during the session.
3) Session recording and retention notes
• Observability (recording transcripts/logs/audio) must be enabled in the project’s Data & privacy settings. Recorded data is stored in the US and retained for 30 days.
If you need a programmatic POST-session workflow
• There is no explicit documented API to fetch transcripts via the Python server SDK in the provided sources. If your goal is automated retrieval after a session, you have two practical options now:
◦ Use the real-time transcription events during the session and store transcripts in your own backend (recommended for automation).
◦ Manually download transcripts from the dashboard for now.
Follow-up questions so I can help further
• Do you need transcripts in real time (during the call) or only after the session ends?
• Are you running agent code where you can handle events like user_input_transcribed? If so, I can provide a fuller code example integrated into your agent loop.
• If you require automated post-session retrieval, would storing transcripts during the session to your own storage (S3, DB) work for your use case?
Tell me which option you prefer and I’ll give the exact Python example or integration steps.
Sources: Streamline troubleshooting with Agent Observability | Transcriber | LiveKit Documentationfancy-florist-18763
02/24/2026, 7:16 PMtall-airline-16756
02/24/2026, 7:17 PMctx.make_session_report() and extract/post transcripts from that serialized report. You want to additionally fetch transcripts from the session after it ends.
Short answer
• Post-session transcripts are available via Agent Observability in the LiveKit Cloud Dashboard (you can view/download JSON there).
• There is no documented server-side API in the provided sources that directly returns session transcripts via the Python Server SDK.
• Best practical solutions: 1) persist transcripts during the session (recommended), or 2) rely on the dashboard/manual download for post-session artifacts. I’ll show how to implement option (1) and how to adapt your existing code to use transcripts produced during the session.
Recommended approaches
1) Capture and store transcripts during the session (recommended)
• Subscribe to the transcription event in your agent runtime and write transcript turns to your backend or S3 as they arrive. This gives you an automated post-session transcript without needing a separate API call after the session ends.
• Example pattern (integrate into your agent session code):
# Pseudocode/event handler style
@session.on("user_input_transcribed")
def on_transcript(transcript):
# Only save final transcripts, to avoid partial fragments
if transcript.is_final:
entry = {
"speaker": "candidate" if transcript.source == "user" else "interviewer",
"timestamp_ms": int(transcript.created_at * 1000) if transcript.created_at else 0,
"text": transcript.transcript,
}
save_transcript_entry_to_db_or_s3(interview_id, entry)
• Advantages: immediate, reliable, and automatable. You control retention and export format.
2) Continue using ctx.make_session_report() but ensure observability/recording is enabled
• Your existing flow that calls ctx.make_session_report() and then serialize_object(...) already pulls transcript-like conversation events from the session report.
• Ensure observability (recording) is enabled in project Data & privacy settings so that session events and transcripts are captured. Recorded observability data is stored for 30 days.
• If make_session_report() reliably returns the events you need, keep your extract_transcript_entries logic but consider persisting the output immediately in your cleanup path (you’re already posting to backend — this is consistent).
3) Dashboard download / manual retrieval
• For occasional or manual workflows, you can open the session in the LiveKit Cloud Dashboard under Agent insights and download transcripts, audio, and logs as JSON/text.
• This is not automated and doesn’t suit fully automatic post-session workflows.
Notes about APIs and egress
• The provided sources do not document a Python Server SDK API that returns session transcripts directly after session end. If automated post-session retrieval via an API is essential, you can:
◦ Store transcripts in your backend during the session (approach #1).
◦ Or, if you enable egress/export to your own storage (planned/available for some flows), you can pull artifacts from that storage. Watch for export/egress features to be available for automated post-session retrieval.
Suggested changes to your current save_session_report flow
• If you prefer the session-report approach, make sure you:
a. Enable observability (recording) for the project/agent so make_session_report() contains transcript events.
b. Immediately persist the serialized report or the extracted transcript entries to your storage (S3/DB) inside your save_session_report so you don’t rely on manual dashboard downloads.
c. Optionally combine both approaches: persist real-time transcripts as they arrive and still generate the session report at the end as a secondary verification/source of truth.
Follow-up questions so I can give copy-paste code
• Do you have access to the agent runtime to add the user_input_transcribed handler (real-time capture)? If yes, I can provide a ready-to-use Python snippet that saves to S3.
• Do you want the transcript entries saved as one JSON file per session, or as per-turn records in a database?
• Is observability/recording currently enabled for your project? If not, do you want steps to enable it?
Tell me which option you prefer and I’ll provide the exact Python example to integrate with your current cleanup flow.fancy-florist-18763
02/24/2026, 9:28 PMtall-airline-16756
02/24/2026, 9:29 PMsave_session_report more robust so it always saves transcripts even if some events are missing.
Before you start
• Ensure Agent Observability / recording is enabled in your project’s Data & privacy settings so session events are captured and the final session report includes conversation events. Recorded data is retained for 30 days.
A. Capture transcripts in real time (recommended) Why: This guarantees you have a machine-readable transcript saved automatically even if report generation later misses something. Store each final turn to S3 or your DB as it arrives.
1) Add a handler in your agent runtime for the transcription event (server-side Python).
• Listen for the user_input_transcribed or equivalent transcription event.
• Save final transcript turns immediately.
Example (outline you can adapt to your agent framework):
def handle_transcript_event(session, interview_id, transcript):
# Only keep final transcript fragments
if not getattr(transcript, "is_final", False):
return
speaker = "candidate" if getattr(transcript, "source", "") == "user" else "interviewer"
timestamp_ms = int(getattr(transcript, "created_at", 0) * 1000) if getattr(transcript, "created_at", None) else 0
entry = {
"speaker": speaker,
"timestamp_ms": timestamp_ms,
"text": getattr(transcript, "transcript", "").strip(),
}
if entry["text"]:
save_transcript_entry(interview_id, entry) # implement below
# Example subscription (replace with your session framework)
@session.on("user_input_transcribed")
def on_user_input_transcribed(transcript):
handle_transcript_event(session, interview_id, transcript)
2) Implement save_transcript_entry to persist to S3 or DB.
• Option: append to an in-memory list per-session and flush on end; or immediately upload/append to S3 (e.g., JSONL) per final turn.
Simple JSONL append to S3 approach:
import json
import boto3
from botocore.exceptions import BotoCoreError, ClientError
s3 = boto3.client("s3")
BUCKET = "your-bucket"
PREFIX = "transcripts/"
def s3_append_jsonl(interview_id, entry):
key = f"{PREFIX}{interview_id}.jsonl"
line = json.dumps(entry) + "\n"
try:
# Try to fetch existing object and append (simple approach - works for small files)
existing = None
try:
resp = s3.get_object(Bucket=BUCKET, Key=key)
existing = resp["Body"].read()
except ClientError as e:
# object may not exist; will create new
existing = None
if existing:
data = existing + line.encode("utf-8")
s3.put_object(Bucket=BUCKET, Key=key, Body=data)
else:
s3.put_object(Bucket=BUCKET, Key=key, Body=line.encode("utf-8"))
except (BotoCoreError, ClientError) as e:
logger.error("Failed to append transcript to S3: %s", e)
Notes:
• For high throughput, use multipart uploads or write to a DB; avoid repeatedly reading/writing the whole object.
• You can also buffer entries in memory and flush on session end.
B. Strengthen your end-of-session save_session_report Why: Keep your existing report generation but make it detect missing transcripts and consolidate with the real-time saved transcripts.
1) Update save_session_report to:
• Generate the LiveKit report as you do.
• Extract events as you already do.
• If no events or few transcripts found, fallback to the transcripts you persisted during the session (S3/DB).
• Combine both sources and dedupe by timestamp or text.
Updated flow (pseudo-code):
def save_session_report(ctx, metadata: dict, egress_id: str = None) -> str | None:
interview_id = metadata.get("interview_id", "unknown")
try:
livekit_report_obj = ctx.make_session_report()
livekit_report = serialize_object(livekit_report_obj)
except AttributeError as e:
logger.error("Failed to create session report (missing attribute): %s", e)
return None
except Exception as e:
logger.error("Failed to serialize session report: %s", e, exc_info=True)
return None
transcript_entries = extract_transcript_entries(livekit_report)
<http://logger.info|logger.info>("Extracted %d transcript entries from session report", len(transcript_entries))
# Fallback: load persisted real-time transcripts from S3/DB if empty or incomplete
if not transcript_entries:
<http://logger.info|logger.info>("No transcripts in session report; loading persisted transcripts as fallback")
persisted = load_persisted_transcripts(interview_id) # implement this
transcript_entries = persisted or []
# Optionally merge and dedupe both sources
persisted = load_persisted_transcripts(interview_id)
if persisted:
combined = merge_and_dedupe_transcripts(transcript_entries, persisted)
else:
combined = transcript_entries
if combined:
<http://logger.info|logger.info>("Posting %d transcripts to backend...", len(combined))
post_transcripts_to_backend(interview_id, combined)
<http://logger.info|logger.info>("Transcripts posted")
else:
logger.warning("No transcript entries found to post")
# serialize and upload full session report as well
s3_key = upload_report_to_s3(interview_id, livekit_report, egress_id)
return s3_key
2) Implement load_persisted_transcripts and merge_and_dedupe_transcripts
• load_persisted_transcripts(interview_id) should read your S3/DB JSONL or JSON file and return a list of entries.
• merge_and_dedupe_transcripts(a, b) should merge lists and remove duplicates by (timestamp_ms, speaker, text) or by fuzzy text match if needed.
Example merge:
def merge_and_dedupe_transcripts(a, b):
seen = set()
merged = []
for entry in (a or []) + (b or []):
key = (entry.get("timestamp_ms"), entry.get("speaker"), entry.get("text"))
if key in seen:
continue
seen.add(key)
merged.append(entry)
merged.sort(key=lambda e: e.get("timestamp_ms", 0))
return merged
C. Additional best practices
• Prefer storing transcripts to your own durable store during the session. That’s the most reliable automation path.
• Keep per-session transcript file naming stable (e.g., transcripts/{interview_id}.jsonl) so your end-of-session job can always find it.
• If you must rely only on ctx.make_session_report(), ensure observability/recording is turned on and test that sample sessions produce the expected conversation_item_added events.
• Use a consistent timezone/epoch handling: store timestamps as UTC ms since epoch.
Questions so I can give a tailored, ready-to-run snippet
• Which persistence do you prefer for real-time entries: S3 (JSONL), RDS/SQL, or a document DB (DynamoDB/Mongo)?
• Are you using an agent framework where you can add @session.on("user_input_transcribed") handlers? If yes, tell me how transcripts are represented (attributes), and I’ll produce exact code for saving to S3 and for the fallback load function.
• Do you want a full example that replaces your S3 append with a safer approach (e.g., buffer + batch upload) for production?
Tell me your preferred storage and I’ll give the exact complete code to plug into your current flow.