what's the point of having room admin grant on a p...
# ask-ai
r
what's the point of having room admin grant on a participant ? what i can do from client side if i'm a room admin ?
t
Thanks for reaching out, simo.dev. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — good question. You're asking what the RoomAdmin grant does and what you can do from the client side if you're a room admin. What RoomAdmin grants • Gives a participant elevated moderation permissions for the room (set on their access token). • Intended for moderators, hosts, or automated agents who need to manage the session. What you can do (client-driven, usually via your backend) • Mute/unmute other participants' published tracks. • Remove/disconnect other participants from the room. • Update other participants' permissions (e.g., allow/deny publishing or subscribing). • Update other participants' metadata. How it works in practice • The grant is encoded in the token (server-side). Client UI (e.g., “mute user”, “remove user”) calls your backend, which uses the LiveKit server APIs/SDK to perform the privileged actions authorized by RoomAdmin. • Client SDKs let you build the UI and trigger those backend calls, but the privileged operations are executed via server-side API calls. If you want, tell me which client SDK you’re using (Web, Android, iOS) and I can outline the exact API/SDK methods and a short flow for implementing a “mute user” or “remove user” button. , with interactive elements
r
i want the agent to not respond i just want to collect call transcriptions
t
Thanks — I’m responding to your request to have the agent not respond and only collect call transcriptions. Yes — you can run an agent in a transcription-only mode. Two recommended approaches: • Disable agent outputs in the room options so it listens and transcribes but does not speak or send text replies: a. Set audio_output = False and text_output = False (or audio_output=False and text_output=True if you want transcriptions as text). b. Keep audio input enabled so speech-to-text runs. c. Transcriptions are published on the
lk.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 elements
r
give me example (python)
t
Thanks — below is a minimal Python example that runs an STT-only agent (no LLM/TTS) that listens in a room and writes final transcriptions to a file. It disables any agent responses by not configuring LLM or TTS.
Copy code
import 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 elements
r
how to check for the admin participant const participants = await client.listParticipants(room) hasAdmin = participants.filter(p=>p.permission.ad)
t
You're close — the permission field is
roomAdmin
. Use
permission?.roomAdmin
. Examples: • Check if any admin exists:
Copy code
const participants = await client.listParticipants(room);
const hasAdmin = participants.some(p => p.permission?.roomAdmin);
• Get all admin participants:
Copy code
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 elements
r
Property 'roomAdmin' does not exist on type 'ParticipantPermission'.ts(2339
t
Thanks — that TypeScript error means your local SDK types don't include
roomAdmin
. 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:
Copy code
console.log(participant);
console.log('permissions:', participant.permission ?? participant.permissions);
1. Use the correct property path (try both) • Newer SDKs use `permission.roomAdmin`:
Copy code
const isAdmin = !!participant.permission?.roomAdmin;
• Some SDKs use `permissions.roomAdmin`:
Copy code
const isAdmin = !!participant.permissions?.roomAdmin;
1. Workaround if types still complain • Use a type-safe cast or index access:
Copy code
const isAdmin = Boolean((participant as any).permission?.roomAdmin);
or
Copy code
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 elements
Thanks — I’m responding to your agent-side 401 websocket error when connecting to wss://agent-dev-nu5nn6qh.livekit.cloud/agent. Likely causes and quick checks • API key/secret mismatch: Ensure the agent’s LIVEKIT_API_KEY and LIVEKIT_API_SECRET exactly match the project credentials (no extra spaces or newlines). • LIVEKIT_URL correctness: Verify LIVEKIT_URL points to the same host (agent-dev-nu5nn6qh.livekit.cloud). • Env access: Confirm the agent process actually reads those env vars (print them or log masked values). • Token generation: If you generate a JWT for the agent, decode it (e.g., jwt.io) and check
iss
(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