calm-optician-60209
04/03/2026, 11:49 AMtall-airline-16756
04/03/2026, 11:49 AMtall-airline-16756
04/03/2026, 11:49 AMmetadata field on the dispatch/job. That metadata can be any freeform string (commonly JSON) and will be available to the agent at runtime as ctx.job.metadata in the agent entrypoint.
◦ This lets you pass caller-specific info (caller ID, account id, routing context, etc.) per inbound call rather than baking it into a static rule.
◦ See: Agent dispatch / CreateAgentDispatchRequest for the metadata field. Agent dispatch
• Inject metadata via SIP headers -> participant attributes (for SIP trunks)
◦ If the inbound call arrives over SIP, map SIP headers to participant attributes in your trunk/connector configuration (for example headers_to_attributes). Those attributes become part of the call/participant and can be consumed by the agent.
◦ You can also map attributes back to SIP headers (attributes_to_headers) when necessary.
◦ This is useful when your SIP provider (Five9, etc.) already sends custom headers with the call and you want those forwarded into LiveKit as per-call metadata.
◦ See: Five9 / headers mapping guide. Five9 integration guide
• Use participant attributes in your room_config
◦ For non-SIP flows, set participant attributes on the incoming participant or set them programmatically when the call arrives. These attributes are accessible to the agent and can carry per-call context.
◦ This is a flexible method when you control the signaling or when another service in your flow can attach attributes at call time.
◦ See: Job lifecycle / participant attributes. Job lifecycle
How the agent reads the data
• For explicit dispatch jobs: read ctx.job.metadata inside the agent entrypoint / handler.
• For participant attributes: read the participant attributes exposed to the agent in the job/room context.
Quick decision guide
• If you control the dispatch call: use explicit dispatch with metadata — simplest and most direct.
• If call arrives from a SIP provider and carries custom SIP headers: use headers->attributes mapping.
• If you need attributes attached after call setup (or from other services): set participant attributes programmatically.
If you want, tell me:
• Which inbound path you use (SIP trunk or other),
• Whether you create dispatches via API or rely on automatic dispatch rules,
and I’ll give a precise code/config example for that flow (API payload or trunk config).
Sources: Agent dispatch | LiveKit Documentation | Job lifecycle | LiveKit Documentation | How to do call transfers with Five9 | LiveKit, with interactive elementscalm-optician-60209
04/03/2026, 11:50 AMtall-airline-16756
04/03/2026, 11:50 AMmetadata on the job,
◦ mapping SIP headers into participant attributes,
◦ or setting participant attributes programmatically.
• That per-call metadata is stored on the job/participant associated with that single call. Agents read the call-specific data from ctx.job.metadata (explicit dispatch) or the participant attributes for that job.
• Concurrency: Multiple jobs may be created simultaneously for the same phone number; each job keeps its own metadata payload and state. There’s no cross-talk between the metadata of separate jobs.
Routing/agent assignment considerations
• If you use automatic/static dispatch rules, ensure your rules and agent capacity support parallel handling of multiple jobs. Static rules don’t mix metadata between calls — they just control routing.
• If you use explicit dispatching via API, create a separate dispatch request per incoming call and include the correct metadata in each request so the agent gets the right context.
• If your SIP provider sends headers, make sure the header-to-attribute mapping is applied per call (SIP headers are per-call), so each call’s attributes remain separate.
Potential pitfalls to watch for
• Shared identifiers: Don’t reuse a single job ID or shared global state for multiple simultaneous calls — give each call its own job/participant context.
• Agent load: If many concurrent calls arrive to the same queue or phone number, ensure you have enough agent capacity or queue logic so calls don’t get dropped or misrouted.
• Metadata size/format: Keep metadata reasonably sized (JSON string is common) and validate fields so agents can reliably parse per-call context.
If you want, I can:
• Show an example API payload for creating separate dispatch jobs with different metadata for three simultaneous calls.
• Or show SIP trunk header->attribute config for per-call headers if you’re using a SIP provider.
Which would you like: an API example or a SIP header mapping example? Also, are you using explicit dispatch or automatic/static dispatch rules today?
, with interactive elementscalm-optician-60209
04/07/2026, 3:27 PMtall-airline-16756
04/07/2026, 3:27 PMCreateSIPParticipant).
c. Pass full context (conversation history) via chat_ctx so the receiving agent sees the job context.
d. Use WarmTransferTask to orchestrate hold, briefing, and move the human into the caller room.
e. Disconnect interim agents so only the human and caller remain.
• Key APIs/examples:
◦ WarmTransferTask (provides orchestration)
◦ CreateSIPParticipant to dial the supervisor
◦ Pass chat_ctx / ctx.job.metadata for context
• When to use: complex issues or when you want the receiving agent pre-briefed.
Cold transfer (straight handoff)
• What it is: Caller is immediately transferred to the target destination (no private briefing).
• How to implement:
a. Use SIP REFER or the transfer API to move the call.
b. Call TransferSIPParticipant / api.sip.transfer_sip_participant with room_name, participant_identity, and transfer_to.
c. Ensure your SIP trunk/provider supports SIP REFER / call transfer and caller ID passthrough if needed.
• Key APIs/examples:
◦ TransferSIPParticipant / transfer_sip_participant API
• When to use: simple forwarding, fast routing, or offloading the call without briefing.
Context & best practices
• Always pass per-call context (use chat_ctx or job metadata) so the receiver has necessary info.
• Use a short announcement/filler speech to smooth perceived latency.
• Verify SIP trunk supports transfers and caller ID passthrough for cold transfers.
• For high-concurrency flows, make each call its own job/participant so contexts remain isolated.
If you want, I can:
• Provide a concrete code snippet for warm transfer in Python or JS, or
• Show the transfer_sip_participant API payload for cold transfer.
Which example would you like?
Sources: The Handoff Pattern for Voice Agents That Replaces IVR Menus | LiveKit | The Human-in-the-Loop (HITL) Pattern for Voice Agents | LiveKit | Handling payments with PCI compliance | LiveKit | Preserving Caller ID During SIP Transfers in LiveKit Agents | Preserve caller ID during SIP transfers with Twilio | LiveKit, with interactive elementscalm-optician-60209
04/07/2026, 4:10 PMtall-airline-16756
04/07/2026, 4:10 PMWarmTransferTask or CreateSIPParticipant can use it when dialing the agent.
If you’d like, tell me:
• Which SIP provider or trunk you use (or none), and
• Whether you want an example of the WarmTransferTask call that references sip_trunk_id — I’ll provide the exact payload/snippet., with interactive elementscalm-optician-60209
04/07/2026, 4:12 PMtall-airline-16756
04/07/2026, 4:12 PMescalate_to_human on SupportAgent that performs a warm transfer.
• Plays a scripted message to the caller: “I’m connecting you with a specialist now…”
• Waits for that message to finish with context.wait_for_playout() so the caller hears the full announcement.
• Calls WarmTransferTask to dial the supervisor number via the specified SIP trunk and carry chat_ctx (full conversation context) into the consultation.
• Returns the WarmTransferTask result so your agent logic can react to success/failure.
What you’ll see at runtime
• Caller hears the announcement and is placed on hold (optionally with hold music if configured).
• LiveKit uses your sip_trunk_id to place an outbound call to SUPERVISOR_PHONE.
• A private consultation room is created and the supervisor is dialed in.
• The supervisor receives the conversation context (from chat_ctx) so they’re briefed before joining.
• When the supervisor accepts, they are moved into the caller’s room and interim agents disconnect, leaving a human-to-human call.
• result contains the transfer outcome (success, failure, or any error) so you can log, retry, or notify the caller.
Notes and checks
• Ensure your SIP trunk supports outbound dialing and caller ID passthrough.
• If you need hold music or to disable caller audio explicitly, configure that in your room/hold settings.
• If you want a code example showing how to inspect result and handle failures, I can provide one.
Would you like a minimal example that handles transfer failures and notifies the caller?
, with interactive elementscalm-optician-60209
04/07/2026, 4:15 PMtall-airline-16756
04/07/2026, 4:15 PMWarmTransferTask. If no sip_trunk_id (or sip_connection) is provided, the transfer will fail because LiveKit can’t place the outbound leg.
What you can do next
• Use a saved outbound trunk: create/configure an outbound SIP trunk in your LiveKit portal (or via API) and pass its ID as sip_trunk_id.
• Or provide a direct SIP outbound config: use sip_connection / SIPOutboundConfig in the WarmTransferTask call to specify hostname, auth, etc., instead of a trunk ID.
• You can also set a default via the environment variable LIVEKIT_SIP_OUTBOUND_TRUNK so code can omit the explicit ID.
If you want, I can:
• Show the portal/API steps to create a trunk and get its ID, or
• Give a concrete code snippet using sip_trunk_id or an example sip_connection payload.
Which would you prefer?
Sources: How to do call transfers with Five9 | LiveKit | Job lifecycle | LiveKit Documentation | sip | livekit/livekit_composite | DeepWiki | Creating SIP Inbound Trunks and Dispatch Rules with Python SDK | The Handoff Pattern for Voice Agents That Replaces IVR Menus | LiveKit, with interactive elementscalm-optician-60209
04/16/2026, 12:34 PMtall-airline-16756
04/16/2026, 12:35 PMWarmTransferTask. It includes imports, optional env var fallback for sip_trunk_id, and basic result handling.
import os
from livekit.agents import Agent, function_tool, RunContext
from livekit.agents.beta.workflows import WarmTransferTask
SUPERVISOR_PHONE = "+15551234567"
# prefer explicit config, fallback to environment variable
SIP_TRUNK_ID = os.getenv("LIVEKIT_SIP_OUTBOUND_TRUNK", "your-sip-trunk-id")
class SupportAgent(Agent):
@function_tool()
async def escalate_to_human(self, context: RunContext):
await self.session.say(
"I'm connecting you with a specialist now. I'll share our conversation so you won't need to repeat anything.",
allow_interruptions=False,
)
await context.wait_for_playout()
try:
result = await WarmTransferTask(
sip_call_to=SUPERVISOR_PHONE,
sip_trunk_id=SIP_TRUNK_ID,
chat_ctx=self.chat_ctx,
)
except Exception as e:
# handle immediate failure to start transfer
await self.session.say("Sorry, I couldn't connect you right now. Please hold while I try again.")
# log or rethrow as needed
raise
# Inspect result for success/failure
if getattr(result, "success", False):
# transfer succeeded; result may include details depending on SDK version
await self.session.say("I have connected you to a specialist. Thank you.")
else:
# transfer failed or was declined
await self.session.say("I couldn't reach the specialist. Would you like to wait or leave a callback number?")
# optional: log result.error or result.reason
return result
Notes
• sip_trunk_id is required (or use a `sip_connection`/`SIPOutboundConfig` alternative) so LiveKit can place the outbound call.
• chat_ctx carries conversation context for the supervisor.
• Add logging and retry/backoff as needed for production., with interactive elementscalm-optician-60209
04/22/2026, 1:47 PMtall-airline-16756
04/22/2026, 1:48 PMcalm-optician-60209
04/23/2026, 9:01 AMtall-airline-16756
04/23/2026, 9:02 AMmin_endpointing_delay and max_endpointing_delay and how they relate to the TTS error ("no audio frames were pushed for text"). Here’s a concise explanation and next steps.
What those settings do
• min_endpointing_delay (seconds): minimum time the system waits after speech generation stops before declaring the utterance finished. Prevents premature endpointing.
• max_endpointing_delay (seconds): maximum time the system will wait for additional audio before forcing endpointing. Prevents hanging when no more audio arrives.
Why they matter for your error
• If min_endpointing_delay is too large relative to when audio frames are produced, the system might expect frames that never arrive and eventually time out in a way that surfaces as “no audio frames were pushed.”
• If max_endpointing_delay is too small, you may cut off streaming before frames arrive, also leading to no-audio behavior for short/slow TTS streams.
Suggested starting values
• Keep min_endpointing_delay small but not zero: 0.2–0.5s is reasonable.
• Keep max_endpointing_delay larger to tolerate network/processing latency: 1.0–2.0s is typical.
Your values (0.3 and 1.5) are within recommended ranges.
Next diagnostics to run
• Confirm the TTS generator actually emits frames: add logging where frames are created and pushed.
• Test with a short known phrase ("Hello world") and verify frames appear.
• Check timing: log timestamps for first/last frame vs. endpoint timers to see if endpointing fires before frames arrive.
• Ensure encoding/format matches expected pipeline (sample rate, codec, Opus vs PCM).
• If using async streaming, add a small artificial delay before endpointing to rule out race conditions.
If you want, paste the small TTS call/streaming snippet and I’ll point out exactly where to log or adjust timers to confirm frames are produced and accepted before endpointing.
, with interactive elementscalm-optician-60209
04/23/2026, 9:03 AMtall-airline-16756
04/23/2026, 9:04 AM