wide-afternoon-22026
02/17/2026, 10:33 AMtall-airline-16756
02/17/2026, 10:33 AMtall-airline-16756
02/17/2026, 10:34 AMwide-afternoon-22026
02/17/2026, 10:36 AMtall-airline-16756
02/17/2026, 10:37 AMsip attributes) from a LiveKit JavaScript agent.
Short answer: Yes. SIP participants expose SIP-specific attributes on the participant object, and you can read those attributes and listen for changes from a LiveKit JS/Node agent.
What to look for
• SIP participants have a participant kind that identifies them as SIP.
• SIP metadata is available on the participant’s attributes object under keys like:
◦ sip.callStatus (e.g., "dialing", "ringing", "active", "hangup")
◦ sip.phoneNumber
◦ sip.callID
◦ sip.trunkID
◦ sip.trunkPhoneNumber
Example (Node/JS agent)
1. Listen for participants joining and check kind.
2. Read the SIP attributes from participant.attributes.
3. Listen for attribute changes to track status updates.
// pseudocode using LiveKit JS/agent-style events
room.on('participantConnected', (participant) => {
// check if participant is a SIP participant (SDK exposes a kind field)
if (participant.kind === 'sip' || participant.kind === 'SIP') {
const attrs = participant.attributes || {};
console.log('SIP attributes on connect:', {
callStatus: attrs['sip.callStatus'],
phoneNumber: attrs['sip.phoneNumber'],
callID: attrs['sip.callID'],
trunkID: attrs['sip.trunkID'],
});
}
});
room.on('participantAttributesChanged', (participant, updatedAttributes) => {
if (participant.kind === 'sip' || participant.kind === 'SIP') {
if ('sip.callStatus' in updatedAttributes) {
console.log('SIP call status changed:', updatedAttributes['sip.callStatus']);
}
// handle other sip.* attribute updates similarly
}
});
Permissions / token notes
• Ensure your agent’s access token includes the necessary SIP grants so it can observe SIP participants and their attributes.
If you want, I can:
• Provide a concrete code sample tailored to the specific LiveKit JS SDK version you’re using (browser SDK vs Node agent).
• Show how to filter existing participants for SIP kind on initial room join.
• Help you handle specific SIP attribute events (for example, translate sip.callStatus changes into application state).
Which would you prefer, and are you using the browser SDK or a server-side Node agent?
Sources: livekit/livekit_composite | DeepWiki | SIP Lifecycle Management Agent | LiveKit Documentation | sip | livekit/livekit_composite | DeepWikiwide-afternoon-22026
02/17/2026, 10:59 AMtall-airline-16756
02/17/2026, 10:59 AMparticipant.attributes. This gives you SIP attributes right away on connect.
◦ Also subscribe to participantAttributesChanged so you catch status updates that happen after connect.
• If you want the info before the agent joins the room (truly pre-dispatch):
◦ Maintain the SIP call metadata in your application (for example, store sip.* attributes in your call record when the call is created). Your dispatch system can query that store and include the SIP details with the dispatch message to the agent.
◦ Or, if your dispatch system can call LiveKit server-side APIs (or inspect room state on the server), it can read participant attributes from the LiveKit room before instructing the agent to connect — then pass those attributes to the agent as part of the dispatch payload.
• If you need near-instant updates for call state transitions (e.g., ring → active):
◦ Combine immediate read-on-connect with attribute-change events for real-time updates.
◦ Optionally push the current state to the agent via your dispatch channel to avoid waiting for the SDK lifecycle.
Practical example (fast read-on-connect)
1. Agent connects to room.
2. Immediately enumerate room.participants and look for participant.kind === 'sip' (or equivalent).
3. Read participant.attributes['sip.callStatus'], etc.
4. Also register participantAttributesChanged to catch updates.
Permissions note
• Ensure the agent’s access token includes SIP/room observation grants so it can see participants and attributes when it connects.
If you tell me which of the three scenarios above matches your flow (pre-dispatch, immediate-on-connect, or server-side pre-read), and whether you’re using a Node agent or browser SDK, I’ll provide a concrete code sample and a recommended dispatch pattern you can implement.wide-afternoon-22026
02/17/2026, 11:00 AMtall-airline-16756
02/17/2026, 11:00 AMkind === SIP (or the SIP participant marker you use).
b. Extract participant.attributes['sip.callStatus'], ['sip.phoneNumber'], ['sip.callID'], ['sip.trunkID'], etc.
c. Include these fields in the dispatch job you send to the agent so the agent has the SIP details immediately.
• Notes:
◦ This requires a server-side method to read room state (your server must be able to access LiveKit room participants/attributes). If you store call metadata in your app DB when the call is created, you can read from there instead.
◦ This avoids waiting for the agent to connect and eliminates race conditions where the agent could join slightly before attributes are available locally.
Option B — Immediate read-on-connect by the agent (if pre-dispatch fetch is not possible)
• Overview: Have the agent connect to the room and immediately enumerate existing participants to find the SIP participant and read its attributes. Also subscribe to attribute-change events to catch rapid updates.
• Steps (agent-side):
a. Agent receives the job and connects to the LiveKit room.
b. Immediately after connect, enumerate room.participants (or the SDK equivalent) and filter for SIP participant (check participant.kind).
c. Read participant.attributes['sip.callStatus'], ['sip.phoneNumber'], ['sip.callID'], ['sip.trunkID'].
d. Register a participantAttributesChanged handler to catch any updates that occur after connect.
• Notes:
◦ This method gives you the attributes effectively immediately after the agent connects; it’s fast but not strictly “before connect.”
◦ To minimize latency, have your agent connect as soon as dispatch is received and read attributes before presenting or routing the job internally.
Practical considerations and best practices
• Token permissions: Ensure the token used by whichever component reads participant attributes (server pre-read or agent) has permission/grants to observe room participants and attributes.
• Race conditions: Attributes are available once the SIP participant exists in the room. If you pre-read too early (before the SIP participant has been created), you’ll not see them. If you rely on server-side reads, trigger the read after the SIP participant join or after LiveKit has set the attributes.
• App-level store: To make dispatch deterministic, store SIP metadata in your application database when the SIP call is created or when LiveKit reports the call. Then the dispatcher reads from your DB and sends attributes in the job payload — this is the most robust approach for strict pre-dispatch requirements.
• Real-time updates: Combine pre-dispatch data with agent-side attribute-change subscriptions for state transitions (ringing → active → hangup) that happen after dispatch.
Quick pseudocode sketches
• Server pre-dispatch read (conceptual)
// server retrieves SIP participant attributes, then sends job to agent
const participants = await livekitServerApi.getRoomParticipants(roomName);
const sipParticipant = participants.find(p => p.kind === 'SIP');
const sipAttrs = sipParticipant ? sipParticipant.attributes : {};
dispatchToAgent(agentId, {
room: roomName,
sipAttributes: {
callStatus: sipAttrs['sip.callStatus'],
phoneNumber: sipAttrs['sip.phoneNumber'],
callID: sipAttrs['sip.callID'],
trunkID: sipAttrs['sip.trunkID'],
}
});
• Agent immediate read-on-connect (conceptual)
// agent connects and immediately reads SIP attributes
await agent.connectToRoom(token, roomName);
const sipParticipant = Array.from(room.participants.values()).find(p => p.kind === 'SIP');
const sipAttrs = sipParticipant ? sipParticipant.attributes : {};
// use sipAttrs right away
room.on('participantAttributesChanged', (participant, updated) => {
if (participant.kind === 'SIP') {
// handle updates like sip.callStatus
}
});
Next step
• Do you want a concrete code sample tailored to the exact LiveKit SDK you’re using (browser JS vs Node agent vs server-side API)? If you prefer the server pre-read pattern, tell me whether you can query room state from your server or if you keep call metadata in an app DB — I’ll provide specific code and dispatch message structure.wide-afternoon-22026
02/17/2026, 4:50 PMwide-afternoon-22026
02/17/2026, 4:51 PMtall-airline-16756
02/17/2026, 4:51 PMawait roomServiceClient.deleteRoom({ room: roomName });
• Python (API client):
await ctx.api.room.delete_room(api.DeleteRoomRequest(room=ctx.room.name))
2) Agent-controlled cleanup flag (if using agent session helpers)
• In some agent session flows you can set session options to delete the room on agent session end (check your session config if using that helper). If available, this can automate deletion when the agent session finishes.
Permissions & notes
• Ensure the caller of DeleteRoom has the correct API privileges (server API key or a token with appropriate room management rights).
• If you need to preserve recordings after deletion, verify egress behavior in your setup (deleting the room will stop active egress).
• For deterministic behavior in your app, prefer explicit DeleteRoom at call end or have your app store call-end state and drive deletion from a central controller.
If you’d like, I can:
• Provide a concrete code snippet tailored to the exact SDK and client you’re using (Node, browser agent, or Python).
• Show the exact request shape for the RoomService/DeleteRoom call for your environment.
Which language or SDK would you like the exact example in?
Sources: Why is there extra audio at the end of my recordings of an agent session? |... | Server lifecycle | LiveKit Documentation | Agent session | LiveKit Documentation | Agents telephony integration | LiveKit Documentation | examples/frontdesk | livekit/livekit_composite | DeepWiki