little-king-41579
02/19/2026, 3:29 AMtall-airline-16756
02/19/2026, 3:29 AMtall-airline-16756
02/19/2026, 3:30 AMcan_publish=False and can_subscribe=True. The participant can receive media but not send it.
◦ Example grant snippet:
.with_grants(api.VideoGrants(
room_join=True,
room=room_name,
can_publish=False,
can_subscribe=True
))
• Allow sending but not receiving (publish-only):
◦ Set can_publish=True and can_subscribe=False. Useful for broadcast or data-only clients.
• Hidden/observe without appearing:
◦ Use hidden=True to keep the participant out of participant lists while still subscribing.
.with_grants(api.VideoGrants(
room_join=True,
room=room_name,
can_publish=False,
can_subscribe=True,
hidden=True
))
• Fine‑grained control between specific participants (server‑side enforcement):
◦ Tokens can’t limit a participant to only subscribe to or publish to specific other participants.
◦ After participants join, your server can:
▪︎ Mute published tracks from specific participants,
▪︎ Remove or eject participants,
▪︎ Update participant metadata/roles and enforce routing logic in your application,
▪︎ Use agent/dispatch patterns to connect particular callers or agents to each other.
◦ If you want selective media routing (e.g., only Alice sees Bob), you’ll implement that server logic and use tokens to grant join/publish/subscribe broadly enough for the server to manage connections.
Next steps — please tell me:
1. Do you want to prevent a client from sending audio/video, receiving audio/video, or both?
2. Do you need the client to be able to appear “hidden” in the participant list?
3. Or do you require true per‑participant selective communication (e.g., this user can only see/hear these specific users)? If so, describe the exact flow (who should see/hear whom, and whether server-side logic can enforce it).
Once you tell me which option you want, I’ll provide the exact token code snippet and outline any server-side calls you’ll need.
Sources: Server-side | livekit/livekit_composite | DeepWiki | Agent dispatch | LiveKit Documentationlittle-king-41579
02/19/2026, 3:36 AMtall-airline-16756
02/19/2026, 3:36 AMparticipant_b joins, or do you want a manual trigger (e.g., agent UI button or server decision)?
2. Are you able to run server-side logic when participants join (webhook or server event) to enforce rules, or do you need a pure token-only solution?
◦ Note: tokens cannot restrict a participant to only communicate with a subset of participants. Dynamic, per-participant blocking requires server-side actions after join.
1. Which SDK / language are you using on the client and server (JavaScript, Python, Go, etc.)? I’ll give code matching your stack.
2. Do you want audio/video only affected, or also data channels / screen share?
Once you answer these, I’ll provide:
• The simplest approach (token grant changes if appropriate), or
• A server-side flow using participant updates (examples: mute participant’s published tracks, or call UpdateParticipant/remove) that runs when participant_b joins, plus code snippets for your SDK.little-king-41579
02/19/2026, 3:45 AMtall-airline-16756
02/19/2026, 3:45 AMparticipant_connected event), while leaving communication between the remote participants intact.
Below is an example using LiveKit’s Agents API objects shown in your earlier snippets. It listens for the participant_connected event and mutes the agent’s local published tracks when a new participant joins. Replace placeholders (STT/LLM/TTS/VAD, agent implementation) with your actual setup.
import asyncio
from livekit.agents import JobContext, AgentSession, Participant
from livekit import rtc
async def mute_agent_tracks(agent_session: AgentSession):
"""
Mute all local published tracks of the agent session.
"""
local_participant = agent_session.room.local_participant
if not local_participant:
return
for track_pub in local_participant.track_publications.values():
# Only act on local track publications
if isinstance(track_pub, rtc.LocalTrackPublication):
# set_muted is async in example API
await track_pub.set_muted(True)
print(f"Muted track: {getattr(track_pub.track, 'name', '<unknown>')}")
async def on_participant_connected(agent_session: AgentSession, participant: Participant):
"""
Callback for when a remote participant connects.
Mute the agent's published tracks so agent stops sending media to others.
"""
# Optionally ignore the agent's own connect events
if participant.sid == agent_session.room.local_participant.sid:
return
print(f"Participant connected: {participant.identity} ({participant.sid})")
await mute_agent_tracks(agent_session)
@server.rtc_session()
async def entrypoint(ctx: JobContext):
"""
Agent entrypoint: start session, register handler, and connect.
"""
# Create/configure AgentSession (replace ... with your config)
session = AgentSession(
stt=..., # configure STT
llm=..., # configure LLM
tts=..., # configure TTS
vad=..., # configure VAD
)
# Register the participant_connected handler.
# Use a lambda wrapper to pass the AgentSession instance into the callback.
def _on_participant_connected(p):
# schedule coroutine, do not block event loop
asyncio.create_task(on_participant_connected(session, p))
# Attach before connecting so early joins are captured
ctx.room.on("participant_connected", _on_participant_connected)
# Start the agent session and connect to room
await session.start(agent=MyAgent(), room=ctx.room) # replace MyAgent() with your agent
await ctx.connect()
# Keep the session running until shutdown
await session.wait_until_stopped()
Key points and considerations:
• Tokens cannot restrict which participants a client may subscribe to; you must mute the agent’s outgoing tracks server-side (as shown) to stop the agent from sending audio/video to everyone.
• Muting only the agent’s published tracks leaves the other participants’ published tracks active, so they can continue to communicate with each other.
• The handler checks and ignores the agent’s own participant connect event to avoid muting on self-connect.
• If you need to unmute the agent later, call await track_pub.set_muted(False) for the relevant publications.
• If you also need to block datachannels or other non-media paths, you’ll need additional application logic (e.g., close data channels or stop sending messages).
If you tell me:
• Which SDK/libraries versions you use (agent runtime/library versions), I can tailor any API calls exactly.
• Whether you want the agent muted only when any new participant joins, or only when a specific participant identity joins, I’ll add that filter into the handler. Sources: Agent session | LiveKit Documentation | Job lifecycle | LiveKit Documentation | Building an Automated IVR Menu Caller | LiveKit Documentation