rich-monitor-70665
04/23/2026, 1:56 PMtall-airline-16756
04/23/2026, 1:56 PMtall-airline-16756
04/23/2026, 1:57 PMrich-monitor-70665
04/23/2026, 2:22 PMtall-airline-16756
04/23/2026, 2:23 PMlk.transcription text stream and can be received on the frontend with a text stream handler.
• Or use manual turn control so the agent never generates replies:
◦ Do not call commit_user_turn() or generate_reply() in your agent logic; the agent will transcribe input but will not produce responses.
If you tell me which SDK/platform you’re using (Python, Node, Web, Android, iOS) and whether you want real-time streaming or post-call transcripts, I’ll give the exact code/snippet and the minimal backend/frontend flow to capture lk.transcription messages.
Sources: Text and transcriptions | LiveKit Documentation | How to Set Up Speech-to-Text Input with Text-Only Output in LiveKit Agents | Turns overview | LiveKit Documentation, with interactive elementsrich-monitor-70665
04/23/2026, 2:35 PMtall-airline-16756
04/23/2026, 2:36 PMimport datetime
import asyncio
from dotenv import load_dotenv
from livekit.agents import AgentServer, cli, AgentSession, Agent, inference, JobContext
load_dotenv()
server = AgentServer()
@server.rtc_session()
async def entrypoint(ctx: JobContext):
ctx.log_context_fields = {"room": ctx.room.name}
# Create an STT-only session (no LLM, no TTS) — agent will transcribe but not respond.
session = AgentSession(
stt=inference.STT(model="deepgram/nova-3-general"),
# Do not set any TTS or LLM here
)
# Handle transcription events. Write only final transcripts.
@session.on("user_input_transcribed")
async def on_transcript(transcript):
if transcript.is_final:
ts = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
line = f"[{ts}] {transcript.transcript}\n"
# append to file (or forward to your storage/service)
with open("user_speech_log.txt", "a") as f:
f.write(line)
# Optionally log or emit to your own backend here
# Start the session in the room. Provide minimal Agent instructions if desired.
await session.start(
agent=Agent(instructions="Transcribe user speech; do not generate replies."),
room=ctx.room
)
# Connect and keep the job alive while room is active
await ctx.connect()
if __name__ == "__main__":
cli.run_app(server)
If you want:
• Real-time forwarding of transcriptions to your frontend instead of a file, tell me which frontend (Web, React, etc.) and I’ll add a short example.
• Post-call transcripts only (not streaming), I can show how to aggregate and save after the call ends. Sources: docs/examples/transcriber/README.md, with interactive elementsrich-monitor-70665
04/23/2026, 3:29 PMtall-airline-16756
04/23/2026, 3:30 PMroomAdmin. Use permission?.roomAdmin. Examples:
• Check if any admin exists:
const participants = await client.listParticipants(room);
const hasAdmin = participants.some(p => p.permission?.roomAdmin);
• Get all admin participants:
const adminParticipants = participants.filter(p => p.permission?.roomAdmin);
If you want, tell me which SDK (Web/Node) and I can show a tiny end-to-end snippet including the client call you use.
Sources: livekit-ffi-node-bindings/proto/participant_pb.d.ts, with interactive elementsrich-monitor-70665
04/23/2026, 3:30 PMtall-airline-16756
04/23/2026, 3:31 PMroomAdmin. Quick steps to fix and verify:
1. Confirm SDK/version
• Which LiveKit SDK and version are you using (npm package name and version)? Older versions may use a different permissions shape.
1. Inspect the participant object at runtime
• Log the participant to see available keys:
console.log(participant);
console.log('permissions:', participant.permission ?? participant.permissions);
1. Use the correct property path (try both)
• Newer SDKs use `permission.roomAdmin`:
const isAdmin = !!participant.permission?.roomAdmin;
• Some SDKs use `permissions.roomAdmin`:
const isAdmin = !!participant.permissions?.roomAdmin;
1. Workaround if types still complain
• Use a type-safe cast or index access:
const isAdmin = Boolean((participant as any).permission?.roomAdmin);
or
const p: any = participant;
const isAdmin = !!p.permission?.roomAdmin;
If you tell me your SDK name/version (or paste the logged participant object), I’ll give the exact code and the preferred typed approach.
Sources: How to Set Up Speech-to-Text Input with Text-Only Output in LiveKit Agents, with interactive elementstall-airline-16756
04/23/2026, 5:00 PMiss (should be your API key) and `exp`/`nbf`.
• Clock skew: Ensure system time is correct on the agent machine (NTP sync). Large skew can cause token rejection.
• Permissions/grants: Make sure the token includes any required grants for connecting the agent endpoint.
Minimal diagnostic steps (run on the agent host)
1. Print env vars (mask secret):
◦ echo $LIVEKIT_URL
◦ echo $LIVEKIT_API_KEY
◦ echo "${LIVEKIT_API_SECRET04}…"
1. Try a simple curl to the agent endpoint to confirm reachability:
◦ curl -v https://agent-dev-nu5nn6qh.livekit.cloud/agent
(You should get a 401 or 400; 404/5xx indicates different problem.)
1. If your agent creates a JWT, log the generated token and decode it on jwt.io to inspect claims.
2. Sync time and re-run:
◦ On mac/linux: sudo ntpdate -u pool.ntp.org (or enable systemd-timesyncd)
If you’ve already verified the web app connects with the same env keys:
• Confirm the web app and agent are using the exact same LIVEKIT_URL/API_KEY/SECRET values and not different config files.
• Check for hidden whitespace/newline differences (e.g., copy-paste from env file can add \r).
If you want, paste (sanitized) the code that creates the websocket/token for the agent or the printed decoded token (don't include the secret) and I’ll point out what to change.
Sources: Troubleshooting 401 Unauthorized and Token Errors | LiveKit, with interactive elements