I have a developer workflow where we're currently ...
# ask-ai
e
I have a developer workflow where we're currently calling using a web interface as a participant but we want to test transfer numbers outwards that hits one of our phones. Since this it not a SIP transfer is the logical action to program into our LLM agent to transfer_number that calls an API to add a new participant into the call by dialing an umber outwards and having the agent drop off the call?
t
Thanks for reaching out, Arthur. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — this is a great question. You're asking whether, when you can't (or don't want to) perform a SIP REFER transfer, your LLM agent should instead call an API to dial out a new participant into the room and then drop off. Short answer: yes — that is the correct pattern. What to do (recommended pattern) • Use the API to create an outbound SIP participant that dials the external phone number (a cold transfer / outbound dial-in). • Wait until the outbound call is answered (use
wait_until_answered
/ equivalent) so you don’t drop before the target answers. • Once the new party is joined in the same LiveKit room, have the agent leave the room (disconnect) so the two parties remain connected without the agent. Why this works • This avoids SIP REFER and performs a cold transfer by bridging a new participant into the room. • The room remains active for the new participant after the agent drops off. • You can preserve caller ID on the outbound leg by passing the appropriate
sip_number
if you need the original caller ID shown to the callee. Practical considerations • Ensure
wait_until_answered
(or equivalent) is used so the agent only leaves after the call is answered. • Make the outbound participant identity distinct (e.g.,
transfer_<number>
) so you can track that participant in the room and handle any post-join logic or cleanup. • If you need the callee to see the original caller ID, set
sip_number
to the original caller’s number when creating the outbound participant. • Handle failures: if the outbound call fails or is unanswered, the agent should notify the caller and either retry or reconnect. Example flow (high level) 1. Agent receives instruction to transfer to +1-555-123-4567. 2. Agent calls the LiveKit API to create an outbound SIP participant that dials +1-555-123-4567 with
wait_until_answered=true
. 3. When the outbound leg answers and joins the room, agent informs the caller (optional) and disconnects. 4. Room stays active between caller and callee. If you want, I can: • Provide a short code snippet for your platform (Node/Python) showing the API call and wait logic. • Suggest error-handling flows (retry, fallback options). • Outline how to preserve caller ID in your outbound request. Which would be most helpful next? Sources: Agents telephony integration | LiveKit Documentation | Preserving Caller ID During SIP Transfers in LiveKit Agents | Outbound Calling Script | LiveKit Documentation
e
Is it best to have the agent make the call the LiveKit API or have an external API perform the action? The LiveKit Agent has credentials to control the room
t
Thanks — I’m responding to your question about whether the LLM agent itself should call the LiveKit API to create the outbound participant, or whether an external API should perform that action given the agent already has credentials. Short recommendation • Have the LLM agent call the LiveKit API directly to create the outbound SIP participant and perform the transfer. This is the simplest, lowest-latency, and most natural approach when the agent already has room control credentials. Why this is the recommended approach • The agent already has the
AgentSession
/
JobContext
and credentials needed to call
create_sip_participant
, so it can perform the action without extra hops. • Agents are designed to act as stateful bridges between LLM decision-making and real-time actions (dialing, adding participants), so initiating the outbound call from the agent aligns with intended patterns. • Direct calls avoid added latency, complexity, and failure points that come from routing through an extra external service. When to consider using an external API instead • You need centralized auditing, richer logging, or regulatory compliance that must be kept outside of ephemeral agent sessions. • You want a single, hardened control plane for access control, rate limiting, or cross-service orchestration (e.g., notify billing systems, update CRM, trigger other downstream workflows). • You need to share a single outbound trunk/credential set that you don’t want exposed to many agent instances for security reasons. Practical best practices if the agent calls LiveKit directly • Use
wait_until_answered
so the agent doesn’t drop before the outbound leg connects. • Give the outbound participant a clear identity (e.g.,
transfer_<phone>
) so it’s easy to track and clean up. • Store credentials securely (environment variables, secrets store) and scope them with least privilege. • Implement failure handling in the agent: notify the caller on dial failure, retry policies, or fallbacks to a human operator. • If caller ID matters, set
sip_number
to the original caller’s number on the outbound request. If you prefer an external API, a hybrid approach can also work: • Agent requests an authenticated orchestration service to create the SIP participant; the service performs the API call and returns status. This gives centralized logging/control but adds latency and complexity. If you’d like, I can: • Provide a short Node or Python snippet showing the agent calling
create_sip_participant
with
wait_until_answered
. • Outline an error-handling flow (retries, notifications, fallback). • Sketch a hybrid architecture that centralizes logging while keeping the agent in control. Which option would you like to see next? Sources: Agents telephony integration | LiveKit Documentation | Agents (Python) | livekit/livekit_composite | DeepWiki | examples/voice_agents | livekit/livekit_composite | DeepWiki
e
Does LiveKit require us to have a configured outbound trunk with a given number to do this outbound call?
t
Thanks — I’m responding to your question about whether LiveKit requires a configured outbound trunk (with a phone number) to make outbound calls for transfers. Short answer • Yes. LiveKit requires a configured outbound SIP trunk (and an associated outbound phone number / caller ID) in order to place outbound PSTN calls. Why • The outbound trunk provides the SIP provider credentials and routing needed to reach the PSTN and to present the outbound caller ID. • Calls created by APIs like
create_sip_participant
or tasks such as
WarmTransferTask
must reference a valid
sip_trunk_id
so LiveKit knows which trunk and credentials to use for the outbound leg. Practical notes • Configure an outbound trunk with your SIP provider or use LiveKit’s phone-number/trunk configuration (where available), and then use that trunk’s ID when making the outbound call. • Include the
sip_trunk_id
when calling the API that creates the outbound participant. • If you need the callee to see the original caller’s number, set the outbound `sip_number`/caller ID appropriately on the outbound request (if your trunk and provider allow that). • Without a valid trunk, LiveKit cannot initiate or route outbound PSTN calls. If you’d like, I can: • Provide a short code snippet showing how to pass
sip_trunk_id
and
sip_number
to
create_sip_participant
. • Outline common trunk setup checklist items (credentials, caller ID provisioning, trunk testing). Which would help you next? Sources: WarmTransferTask | LiveKit Documentation | Agents telephony integration | LiveKit Documentation | Outbound Calling Script | LiveKit Documentation
e
How do I access the API within the agent code to use create_sip_participant? is this part of the agent context?
t
You're asking how to call
create_sip_participant
from inside your LLM agent — specifically whether the LiveKit API is available through the agent context. Short answer: yes — the agent has access to the LiveKit API via the session/job context (e.g.,
AgentSession
/
JobContext
), and you can call
create_sip_participant
directly from your agent code. How it fits in the agent • The agent session exposes a
JobContext
(or similar) that includes authenticated API clients and helpers for controlling the room. • Use that API client to call
create_sip_participant
with the
sip_trunk_id
, destination number, and options such as
wait_until_answered
. Minimal example (Python-style pseudocode)
Copy code
async def transfer_call(job_ctx, phone_number: str):
    # job_ctx.api is the LiveKit API client available in the agent context
    resp = await job_ctx.api.sip.create_sip_participant(
        api.CreateSIPParticipantRequest(
            room_name=job_ctx.room.name,
            sip_trunk_id="your_sip_trunk_id",
            sip_call_to=phone_number,
            participant_identity=f"transfer_{phone_number}",
            wait_until_answered=True,
            sip_number="original_caller_number"  # optional: preserve caller ID
        )
    )
    return resp
Key fields to include • `room_name`: the LiveKit room where the transfer should join. • `sip_trunk_id`: the outbound trunk ID you configured (required to place outbound PSTN calls). • `sip_call_to`: the target phone number to dial. • `participant_identity`: a distinct identity for the new participant so you can track/clean up. • `wait_until_answered`: recommended so the agent only drops after the call is answered. •
sip_number
(optional): set to original caller ID if your trunk/provider allows it. Practical tips • Use the API client provided by
job_ctx
(or
AgentSession
) rather than instantiating a separate client — it already has the proper credentials and context. • Keep credentials scoped and stored securely (environment variables / secrets). The agent framework provides the authenticated client so you don’t need to re-authenticate inside the agent code. • Implement error handling: handle unanswered calls, trunk errors, and timeouts, and notify the caller if the transfer fails. • After confirming the outbound participant has joined, disconnect the agent from the room so the call continues between the two parties. If you’d like, I can: • Provide a Node.js example matching your stack. • Show a fuller agent function with error handling and caller notification steps. Which would you prefer?