how to cold transfer a call to sip trunk ?
# ask-ai
r
how to cold transfer a call to sip trunk ?
t
Thanks for reaching out, Saurabh. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — you’re asking how to perform a cold transfer (agent drops, call handed off) from a LiveKit call to a SIP trunk. Below are the exact steps you can follow. 1. Confirm your SIP trunk supports SIP REFER (call transfers) • Make sure your SIP provider/trunk allows transfers via SIP REFER. For example, Twilio requires enabling both “Call Transfer (SIP REFER)” and “Enable PSTN Transfer” on the trunk. 1. Use LiveKit’s TransferSIPParticipant API • Identify the SIP participant in the room (the participant representing the current call). • Call the
transfer_sip_participant
method with a
TransferSIPParticipantRequest
including: ◦ `room_name`: the room where the call is happening ◦ `participant_identity`: the SIP participant’s identity ◦ `transfer_to`: the destination SIP URI (e.g.,
<sip:payment-ivr@your-pci-provider.com>
) or a
tel:
number Example (Python-like):
Copy code
await job_ctx.api.sip.transfer_sip_participant(
    api.TransferSIPParticipantRequest(
        room_name=job_ctx.room.name,
        participant_identity=sip_participant.identity,
        transfer_to="<sip:payment-ivr@your-pci-provider.com>",
    )
)
1. Expected behavior after initiating the transfer • The agent session ends and the call is handed off to the destination (this is a cold transfer — the agent does not stay on the line). 1. Verify trunk configuration and caller ID behavior • Ensure your SIP trunk is configured to allow REFER and preserve or set caller ID as needed. If the trunk rewrites caller ID or doesn’t allow transfers, the transfer can fail or present the wrong caller ID. If you’d like, tell me: • which SIP provider you’re using (e.g., Twilio), and • the destination format you plan to use (
sip:
URI vs
tel:
), and I’ll provide provider-specific notes and any extra configuration tips. Sources: Handling payments with PCI compliance | LiveKit | Agents telephony integration | LiveKit Documentation | Preserve caller ID during SIP transfers with Twilio | LiveKit
r
how to get livekit ip ?
t
Thanks — you’re asking how to “get LiveKit IP.” Do you mean the public IP address for a LiveKit server/service (for firewall rules), the IPs used by LiveKit Cloud egress, or the IP of a specific self‑hosted instance? A quick clarification will let me give the exact steps you need. Short answers by use case: • If you need LiveKit Cloud static IP blocks (for firewall whitelisting): LiveKit Cloud publishes regional egress IP blocks you can allow. Example blocks used by regions include
143.223.88.0/21
and
161.115.160.0/19
. If this is what you need, tell me which region (US, EU, India, etc.) and I’ll point to the exact CIDRs for that region. • If you’re using a self‑hosted LiveKit server and want its public IP: check the public IP assigned by your cloud provider or DNS record for the VM/load balancer. For example, on AWS you can view the Elastic IP in the EC2 console or run
curl <http://ifconfig.me|ifconfig.me>
from the server to show its public IP. • If you need the IP for a specific room/participant or ephemeral media connection: LiveKit normally uses domain names and WebRTC (ICE) for media; clients connect via hostnames (your LiveKit domain) and peer ICE candidates. There’s generally no fixed per‑call IP you need to hardcode. Tell me which of the three (Cloud egress blocks, self‑hosted server public IP, or per‑call/media IP) you mean and which region or provider you’re using, and I’ll give precise steps or the exact CIDRs to use. Sources: Support data sovereignty in India | LiveKit | Running LiveKit on AWS
r
for sip termination to zoiper
t
Thanks — you’re asking about SIP termination to Zoiper. To give precise steps, I need one clarification: which “LiveKit IP” do you need for Zoiper? Please pick one of the options below (or describe your situation): • You need LiveKit Cloud egress IP blocks (CIDRs) to whitelist in your firewall so Zoiper or your SIP trunk can reach LiveKit. If so, tell me the LiveKit Cloud region you’re using (US, EU, India, etc.). • You’re using a self‑hosted LiveKit server and need the server’s public IP (or DNS) so Zoiper can register/terminate calls. If so, tell me your hosting provider (AWS, GCP, DigitalOcean, etc.) or whether you use a load balancer/elastic IP. • You mean the destination SIP URI or phone number format to send from LiveKit to Zoiper (for cold transfer/termination). If so, confirm whether Zoiper is registering to a SIP URI on your network or expecting calls via a PSTN/SIP trunk. Once you tell me which case applies, I’ll provide step‑by‑step instructions: • CIDRs for LiveKit Cloud egress (by region) if you need firewall rules, • How to find and configure a self‑hosted server’s public IP/DNS and Zoiper SIP settings, • Or the exact transfer/termination format and example LiveKit API call to send the call to Zoiper.
r
send from LiveKit to Zoiper (for cold transfer/termination) and Zoiper is registering to a SIP URI on twilio
t
Thanks — you want to cold transfer a LiveKit call to Zoiper, where Zoiper is registered to a Twilio SIP URI. Below are concise, actionable steps and checks to make that transfer work. 1) Ensure Twilio trunk/URI supports REFER (SIP transfer) • In your Twilio trunk settings enable Call Transfer (SIP REFER) and Enable PSTN Transfer. Twilio needs these enabled to accept REFER-based transfers from LiveKit. 2) Confirm Zoiper is properly registered to the Twilio SIP URI • Verify Zoiper is registered and can receive inbound calls via the SIP URI (e.g.,
<sip:user@your-trunk.sip>.twilio.com
). Test an inbound call via Twilio to ensure Zoiper rings and answers. 3) Prepare the transfer destination format • Use a SIP URI as the transfer target. Example: ◦
sip:your-zoiper-username@your-twilio-sip-domain
• Alternatively, you can use a
tel:
number if you route through PSTN, but since Zoiper registers to a Twilio SIP URI, prefer the
sip:
URI. 4) Use LiveKit’s TransferSIPParticipant API to initiate a cold transfer • Identify the SIP participant in the LiveKit room (the participant representing the call). • Call
transfer_sip_participant
with
room_name
,
participant_identity
, and
transfer_to
set to the SIP URI. Example (Python-like):
Copy code
await job_ctx.api.sip.transfer_sip_participant(
    api.TransferSIPParticipantRequest(
        room_name=job_ctx.room.name,
        participant_identity=sip_participant.identity,
        transfer_to="sip:your-zoiper-user@your-twilio-sip-domain",
    )
)
• This initiates a cold transfer: the agent drops and the call is handed off to the destination. 5) Post-transfer behavior and verification • After the transfer, confirm Zoiper receives the incoming call and the session completes. • If the transfer fails, check: ◦ Twilio trunk logs for rejected REFERs or authentication issues. ◦ Whether Twilio rewrote or blocked the REFER or caller ID. ◦ LiveKit logs for errors from the
transfer_sip_participant
request. 6) Common troubleshooting checks • Caller ID/From header: Some trunks rewrite or require specific From headers. Ensure Twilio accepts the From header LiveKit sends or configure Twilio to accept/rewrite appropriately. • Authentication: Ensure Twilio trunk will accept calls for the destination SIP URI without additional auth or that Zoiper’s registration matches Twilio routing rules. • Network/firewall: If any intermediate firewall is present, ensure Twilio and LiveKit egress/ingress are allowed. (LiveKit Cloud uses regional egress IP blocks; if you need those CIDRs, tell me your LiveKit region and I’ll provide them.) If you’d like, I can: • Provide an example
transfer_to
URI formatted specifically for your Twilio domain, • Walk through reading Twilio’s transfer logs if you paste an error snippet, • Or show a short example in another language (Node/Go) if you need it. Which would help most right now?
r
getting 403
t
Thanks — I know a 403 during a cold transfer is frustrating. I’ll help you narrow this down and fix it. First I’ll list the most common causes and quick checks, then ask for a few specific bits of info so I can pinpoint the issue. Quick checks (most common causes of 403 on SIP transfer) • Twilio trunk permissions: Ensure Call Transfer (SIP REFER) and Enable PSTN Transfer are enabled for the Twilio trunk you’re using. • Authentication / routing on Twilio: Make sure Twilio will accept an incoming REFER from LiveKit for the destination SIP URI. If Twilio expects authenticated requests or has routing rules that reject the REFER, it can return 403. • From/Caller-ID headers: Some trunks reject REFERs where the From header or caller ID isn’t allowed. Check if Twilio rewrites or blocks the From header LiveKit sends. • Transfer target formatting: Confirm
transfer_to
is a valid SIP URI Twilio will route to (e.g.,
<sip:user@your-trunk.sip>.twilio.com
) — malformed URIs can cause rejections. • LiveKit credentials/permissions: Confirm the LiveKit transfer API call is authorized and that the SIP participant identity is correct (so LiveKit is actually issuing the transfer to Twilio). • Firewall/network: If you have IP restrictions, ensure LiveKit or your server can reach Twilio; some networks may respond with 403-like behavior when a service is blocked. What I need from you to diagnose 1. The exact error details or logs: ◦ LiveKit logs around the
transfer_sip_participant
call (error message, stack, HTTP response if present). ◦ Twilio SIP Trunk logs showing the incoming REFER or call attempt and the 403 reason (Twilio’s debugger/log timestamp helps). 1. The
transfer_to
value you are using (obscure any sensitive parts if needed). Example:
<sip:zoiper-user@your-trunk.sip>.twilio.com
. 2. Confirmation that Twilio trunk has Call Transfer (SIP REFER) and PSTN Transfer enabled. 3. Whether Zoiper registers directly to Twilio (yes/no) and any custom authentication or IP ACLs on Twilio. 4. If possible, the time (UTC) when you attempted the transfer so I can match logs you paste. Immediate troubleshooting steps you can try now 1. Reproduce and capture logs: ◦ Invoke the transfer and capture LiveKit logs and Twilio request logs for the same timestamp. 1. Test a manual INVITE via Twilio to the same SIP URI to confirm Twilio will route a normal call to Zoiper. 2. Try transferring to a
tel:
number (if allowed) to verify whether REFERs in general are accepted by Twilio from LiveKit. 3. Check Twilio trunk Auth/ACLs: ensure the destination URI is permitted and that Twilio won’t require extra authentication for that target. If you paste: • the Twilio SIP log entry showing the 403 (or the LiveKit error output), and • the
transfer_to
value you used, I’ll analyze the headers and give a precise fix (e.g., adjust From header, enable a Twilio setting, or change the transfer URI format).
r
not using twilio trunk but sip domain
t
Thanks — I’m responding to your issue: cold transferring from LiveKit to Zoiper where Zoiper registers to a SIP domain (not Twilio) and you’re getting a 403. I’ll walk through the most likely causes, concrete checks to run, and the exact LiveKit-side steps you should confirm. What to check first (most common causes of 403 on SIP transfer) • SIP REFER support: Your SIP server/domain must accept SIP REFER requests. If REFER is disabled or restricted, transfers will be rejected with 403. • Authentication/ACLs: Your SIP domain must allow the transfer request from LiveKit — either by accepting credentialed requests or by whitelisting LiveKit’s egress IPs. • Transfer target format: Use a proper SIP URI (e.g.,
<sip:user@your-sip-domain.com>
) as the
transfer_to
. Malformed URIs or unsupported schemes can be rejected. • From/Caller-ID and headers: Some SIP servers enforce policies on the
From
,
Contact
, or
Referred-By
headers. If these look unexpected, the server may return 403. LiveKit-side steps to verify 1. Use the Transfer API correctly: ◦ Call
transfer_sip_participant
with
room_name
,
participant_identity
, and: ▪︎
transfer_to="<sip:zoiper-user@your-sip-domain.com>"
◦ This initiates a cold transfer (agent drops, call handed off). 1. Ensure your LiveKit access token has SIP call permissions: ◦ Include the SIP grant (SIPCall = true) in the token so SIP operations are permitted. 1. Confirm how LiveKit authenticates to your SIP domain: ◦ If your SIP server requires IP-based allowlisting, ensure LiveKit’s outbound IPs are whitelisted. ◦ If credential-based auth is required, ensure LiveKit is configured with the correct SIP credentials. Concrete checks to run now 1. Reproduce the transfer and capture logs: ◦ Copy the LiveKit error output for the failed
transfer_sip_participant
call. ◦ Collect the SIP server logs for the same timestamp to see the incoming REFER and the exact 403 reason. 1. Inspect the REFER request on your SIP server: ◦ Look at
Refer-To
,
From
,
Referred-By
, and
Contact
headers in the incoming request. Note any headers your server rejects or flags. 1. Test a manual REFER (or INVITE) from an external client: ◦ Use a SIP testing tool (sipp, sofia, or another SIP endpoint) to send a REFER to your domain in the same format LiveKit would. If that also yields 403, the issue is on the SIP server side (policies/auth). 1. Temporarily relax restrictions (if possible): ◦ Allow the domain/user in your SIP server policy to accept REFERs from LiveKit IPs or credentials, then retry to confirm policy is the blocker. Likely fixes depending on root cause • If SIP server blocks REFERs: Enable or allow REFER handling for the domain/target user. • If authentication fails: Either whitelist LiveKit egress IPs or configure credential-based auth in LiveKit to match your SIP trunk credentials. • If headers are rejected: Adjust your SIP server policy to accept the `From`/`Refer-To` values LiveKit sends or configure header rewriting on your SIP server. • If URI format is wrong: Use
<sip:user@your-sip-domain.com>
exactly (no extra params) as
transfer_to
. If you share two things I can give a precise fix 1. The
transfer_to
value you used (obscure credentials/user if needed). 2. The SIP server log entry for the incoming REFER showing the 403 (or the LiveKit error log around the same time). With those I’ll: • Inspect the headers and point out which header or value likely triggered the 403. • Give the exact change (allowlist, header rewrite, or token/credential change) to make the transfer succeed. Sources: Firewall Tips for LiveKit Connectivity | LiveKit | Pass UUI data to Genesys Cloud during SIP transfers | LiveKit | Handling payments with PCI compliance | LiveKit | server-sdk-kotlin | livekit/livekit_composite | DeepWiki | How VICIdial handles bridging, transfers, and SIP calls | LiveKit
r
zoiper authenticates with username/password
t
Thanks — you said Zoiper authenticates with username/password. That helps narrow this down. Below are the precise checks and steps to resolve the 403 when doing a cold transfer from LiveKit to a SIP endpoint that requires credential authentication. Short summary of what to do • Configure LiveKit to authenticate to your SIP domain using the SIP username/password that Zoiper uses (or a trunk credential that routes to Zoiper). • Ensure your SIP server accepts SIP REFERs from that authenticated account. • Verify token grants and the exact
transfer_to
SIP URI format. • Capture the SIP REFER on your server to confirm the rejection reason if it still fails. Step-by-step checklist 1) Configure SIP credentials in LiveKit • Add the SIP trunk/account credentials (username/password) in LiveKit’s SIP agent or trunk configuration so LiveKit can authenticate outbound requests to your SIP domain. If LiveKit is not authenticating, your SIP server will reject the REFER with 403. • Ensure the credential used by LiveKit maps to the destination (Zoiper) or a routing rule that delivers calls to Zoiper’s registered user. 2) Ensure the access token allows SIP operations • When issuing access tokens for the agent performing the transfer, include the SIP grant (
SIPCall = true
). Without this SIP grant, LiveKit may not be permitted to run SIP operations. 3) Use the correct transfer target format • Use a SIP URI as
transfer_to
, for example: ◦ transfer_to = "sip:zoiper-user@your-sip-domain.com" • Do not use malformed URIs or add unexpected params that your SIP server may reject. 4) Confirm SIP REFER is allowed for that credential • Your SIP server must accept REFERs when the request is authenticated by the credential LiveKit presents. • Some servers allow INVITEs from an authenticated account but block REFERs. Enable or permit REFER for the account/trunk used. 5) Whitelist IPs if you rely on IP authentication • If your SIP server requires IP allowlisting instead of credentials, ensure LiveKit’s outbound IPs are whitelisted. If you use credential auth, this is less likely needed — but check both. 6) Inspect headers that commonly trigger 403 • If authentication succeeds but your server still returns 403, inspect the incoming REFER headers on your SIP server: ◦ Refer-To, From, Referred-By, Contact • Some servers enforce policies on
From
or
Referred-By
values. If these headers don’t align with what your server expects, configure the server to accept or rewrite them. 7) Reproduce and collect logs • Reproduce the failing transfer and capture: ◦ LiveKit transfer error logs (timestamped) ◦ SIP server logs for the incoming REFER (full request and reason for 403) • The SIP server log will usually say whether the 403 is due to auth failure, denied method (REFER), or policy. If you want, I can give a focused fix — please share: • The exact
transfer_to
value you used (mask any sensitive parts). • The SIP server log entry for the incoming REFER that shows 403 (or the LiveKit error output at the same timestamp). What I’ll do with those: • Check if LiveKit is sending auth credentials or not, and whether the REFER headers look acceptable to your SIP domain. • Recommend the exact config change: add credentials to LiveKit, adjust server policies to accept REFER for that user, or adjust headers/caller ID rewriting. If you’d prefer, I can also walk you through a quick test using a SIP tool (sipp) to simulate an authenticated REFER to your SIP domain so we can confirm whether the server or LiveKit is the blocker. Which would you like to try next?
Thanks — you shared the exact CLI command and the error:
rpc error: code = PermissionDenied desc = sip status: 403: Forbidden
. I’ll help you pinpoint why LiveKit is being rejected and what to change. Why this 403 commonly happens (short) • LiveKit’s transfer request is being rejected by your SIP domain because of authentication/authorization or SIP policy (REFER not allowed). • The most frequent causes here are: LiveKit not presenting valid SIP credentials for the outbound request, the token used lacks the SIP grant (so LiveKit isn’t allowed to perform SIP operations), or your SIP server refuses REFERs from the authenticated account. Concrete next steps to resolve 1) Confirm the access token used by the CLI has the SIP grant • Ensure the agent token includes the SIP grant (
SIPCall = true
). Without that, LiveKit will not be permitted to initiate SIP transfers and you can get PermissionDenied/403. • If you generate tokens programmatically, add the SIP grant to the token before running the CLI command. 2) Configure SIP credentials for LiveKit outbound calls • Your SIP domain requires username/password auth for Zoiper. Make sure LiveKit is configured to use those credentials (or a trunk credential that routes to Zoiper) when sending outbound SIP requests. • If your LiveKit project has a SIP trunk/agent configuration area, add the credential there so LiveKit will authenticate when sending the REFER/INVITE. 3) Verify your
--to
format • Use a clean SIP URI without mailto/encoding. Example: ◦ --to "sip:+12762219741@lk-test-saurabh.sip.twilio.com" • Ensure no extra brackets or mailto formatting are used in the CLI value. 4) Ensure your SIP server accepts REFER from that credential • Confirm that the account LiveKit authenticates as is permitted to send REFER requests and that the SIP server routes REFER to Zoiper’s registered user. • Some servers permit INVITE but block REFER for security—enable REFER for the account if needed. 5) Capture logs for the failing attempt • Re-run the transfer and capture: ◦ LiveKit server logs around the CLI call (timestamp). ◦ Your SIP server logs for the incoming REFER/INVITE showing the 403 reason. • The SIP server log will usually say whether rejection was due to auth, method blocked, or header policy. Quick checklist to try now • Regenerate the agent token with SIP grants and retry the same CLI command. • Confirm LiveKit is configured with the SIP username/password used by Zoiper (or a routing credential). • Use the plain SIP URI in
--to
, e.g.
<sip:+12762219741@lk-test-saurabh.sip>.twilio.com
. • If it still 403s, paste the LiveKit server log entry and the SIP server log for the same timestamp (you can redact secrets); I’ll inspect headers and tell you exactly which value to change. If you’d like, I can show an example of creating a token with the SIP grant or a sample CLI invocation after token creation—tell me which you prefer.
r
Your SIP domain requires username/password auth for Zoiper. Make sure LiveKit is configured to use those credentials (or a trunk credential that routes to Zoiper) when sending outbound SIP requests. , how ?
t
Thanks — you want concrete steps for configuring LiveKit to authenticate outbound SIP requests with username/password so cold transfers to Zoiper succeed. Below are the exact actions to take (API/CLI examples and quick checks). Summary • Create an outbound SIP trunk in LiveKit containing the SIP domain plus the username/password LiveKit should use to authenticate. • Use that trunk when initiating outbound calls or transfers. • Ensure the access token used for the operation includes the SIP grant (
SIPCall = true
). Step 1 — Create an outbound SIP trunk with credentials • Use the LiveKit API/CLI to create a SIP outbound trunk and include the authentication username and password that your SIP server (the domain Zoiper registers to) expects. Example (Python-like SDK):
Copy code
from livekit import api

await lk_api.sip.create_sip_outbound_trunk(
    api.CreateSIPOutboundTrunkRequest(
        trunk=api.SIPOutboundTrunkInfo(
            name="Zoiper-Trunk",
            sip_uri="sip:<http://your-sip-domain.com|your-sip-domain.com>",
            auth_username="zoiper_username",
            auth_password="zoiper_password",
            # optional: allowed_addresses = ["<optional ip>"]
        )
    )
)
Example (CLI):
Copy code
lk sip outbound create \
  --name "Zoiper-Trunk" \
  --sip-uri "sip:<http://your-sip-domain.com|your-sip-domain.com>" \
  --auth-username "zoiper_username" \
  --auth-password "zoiper_password"
Step 2 — Use the trunk when initiating the outbound call/transfer • When you create a SIP participant or initiate the transfer, reference the
sip_trunk_id
of the trunk you created so LiveKit sends the outbound request authenticated with those credentials. Example creating an outbound participant (used for transfer or direct outbound call):
Copy code
await ctx.api.sip.create_sip_participant(
    api.CreateSIPParticipantRequest(
        room_name="current_room",
        sip_trunk_id="ST_outbound_trunk_id",   # ID returned when creating trunk
        sip_call_to="<sip:zoiper-user@your-sip-domain.com>",
        participant_identity="outbound_to_zoiper",
        wait_until_answered=True,
    )
)
For cold transfer specifically: • Use the same
transfer_sip_participant
call, but ensure LiveKit is configured with the outbound trunk so the REFER/INVITE flows out using the trunk credentials. Step 3 — Ensure token grants and permissions • The agent or service invoking the transfer must have an access token that includes the SIP grant: ◦
SIPCall = true
• Without the SIP grant, LiveKit will deny SIP operations and you can see PermissionDenied/403. Step 4 — Verify your SIP server accepts authenticated REFERs/INVITEs • Confirm that the credential you used allows sending REFER (some SIP servers permit INVITE but block REFER for certain accounts). • If your SIP server enforces header policies, ensure it accepts the
From
,
Refer-To
, and
Referred-By
headers LiveKit sends, or add a rule to accept/rewite them for the authenticated account. Step 5 — Test and collect logs if still failing • Reproduce the transfer and check: ◦ LiveKit logs for the transfer call (timestamp). ◦ Your SIP server logs for the incoming authenticated request and the reason for 403 (authentication failure, method not allowed, header policy). • If it still fails, paste: ◦ The `sip_trunk_id`/`transfer_to` value (you can redact passwords), ◦ The SIP server log entry showing the 403 (redact secrets). I’ll inspect the headers and tell you exactly whether LiveKit is failing to send credentials or if the SIP server is rejecting a header/method. Quick checklist to try now • Create outbound trunk with `auth_username`/`auth_password`. • Use that trunk’s ID in your outbound call/transfer request. • Regenerate and use an access token that has
SIPCall
grant. • Retry and check the SIP server logs for the incoming request details. If you want, tell me: • Which method you prefer (CLI or SDK) and your LiveKit region, • The exact
transfer_to
SIP URI (masking sensitive parts), and I’ll provide the precise CLI/SDK command and a sample token snippet ready to run. Sources: Python SDKs | livekit/livekit_composite | DeepWiki | Agents telephony integration | LiveKit Documentation | server-sdk-kotlin | livekit/livekit_composite | DeepWiki
r
this is needed for cold transfer ?
t
Yes — if your SIP domain requires username/password authentication for Zoiper, you must configure LiveKit to use those credentials for the cold transfer to succeed. Why: • A cold transfer from LiveKit sends a SIP REFER/INVITE to your SIP domain. If that domain requires authentication, unauthenticated requests will be rejected (403). • Configuring an outbound SIP trunk with the username/password ensures LiveKit sends the correct Digest authentication headers when it performs the transfer. What you must do (high level) • Create an outbound SIP trunk in LiveKit that contains the SIP domain and the auth credentials (username/password). • Use that trunk when initiating outbound calls or transfers (reference the trunk ID in the API call). • Ensure the agent’s access token includes the SIP grant (
SIPCall = true
) so LiveKit is allowed to perform SIP operations. Quick checklist and minimal steps 1. Create outbound trunk with credentials (CLI or API) ◦ sip_uri = "sip:your-sip-domain.com" ◦ auth_username = "zoiper_username" ◦ auth_password = "zoiper_password" 1. Use the trunk for the transfer ◦ When calling
transfer_sip_participant
or creating an outbound SIP participant, set
sip_trunk_id
to the trunk you created and
transfer_to
to the SIP URI (
<sip:zoiper-user@your-sip-domain.com>
). 1. Ensure token grants ◦ Access token used for the transfer includes
SIPCall = true
. 1. Verify server accepts REFERs for that credential ◦ Confirm your SIP server allows REFER from that authenticated account or adjust policy. If you want, I can: • Provide the exact CLI or SDK commands for your environment (Python/Node/CLI). • Show an example of creating a token with the SIP grant. • Inspect logs if you paste the LiveKit error and the SIP server log for the failed REFER. Which of these would help you next?
r
Use the same
transfer_sip_participant
call, but ensure LiveKit is configured with the outbound trunk so the REFER/INVITE flows out using the trunk credentials., how ?
t
You’re asking how to make sure a cold transfer (
transfer_sip_participant
) uses the outbound trunk credentials (username/password) so your SIP domain (Zoiper) accepts the REFER/INVITE. Below are the concrete steps: create an outbound trunk with credentials, confirm LiveKit will use that trunk for outbound SIP signaling, and call the transfer API referencing the trunk. I include CLI and SDK examples and quick verification steps. Step 1 — Create an outbound SIP trunk with username/password • Create a SIP outbound trunk in LiveKit containing: ◦ sip_uri: your SIP domain (e.g.,
sip:<http://your-sip-domain.com|your-sip-domain.com>
) ◦ auth_username / auth_password: the credential LiveKit should present ◦ optional: allowed_addresses or other routing options if needed Example (CLI)
Copy code
lk sip outbound create \
  --name "Zoiper-Trunk" \
  --sip-uri "sip:<http://your-sip-domain.com|your-sip-domain.com>" \
  --auth-username "zoiper_username" \
  --auth-password "zoiper_password"
Example (Python SDK — illustrative)
Copy code
from livekit import api

resp = await lk_api.sip.create_sip_outbound_trunk(
    api.CreateSIPOutboundTrunkRequest(
        trunk=api.SIPOutboundTrunkInfo(
            name="Zoiper-Trunk",
            sip_uri="sip:<http://your-sip-domain.com|your-sip-domain.com>",
            auth_username="zoiper_username",
            auth_password="zoiper_password",
        )
    )
)
# note resp will include the trunk id (e.g., resp.trunk.id)
Step 2 — Note the trunk ID • Save the returned trunk id (e.g.,
ST_abcdef123
). You’ll reference this when creating outbound calls or performing transfers. Step 3 — Ensure your token has SIP permissions • The agent/service initiating the transfer must have an access token that includes the SIP grant (SIPCall = true). Without it, LiveKit will deny SIP operations. Step 4 — Initiate the cold transfer so LiveKit uses that trunk • When doing a transfer, instruct LiveKit to use the outbound trunk by creating an outbound SIP participant through the trunk or by ensuring the trunk selection is applied to the transfer flow. Two common patterns: A) Preferred pattern — create an authenticated outbound call via the trunk, then transfer: 1. Create an outbound SIP participant using
sip_trunk_id
and
sip_call_to
(this makes LiveKit send an authenticated INVITE via that trunk). 2. Transfer the LiveKit participant to that created SIP participant (or use the trunk in the transfer API if your environment supports specifying trunk on transfer). Example creating outbound participant (Python-like):
Copy code
out_part = await ctx.api.sip.create_sip_participant(
    api.CreateSIPParticipantRequest(
        room_name="room1",
        sip_trunk_id="ST_abcdef123",            # trunk created in step 1
        sip_call_to="<sip:zoiper-user@your-sip-domain.com>",
        participant_identity="out_to_zoiper",
        wait_until_answered=False,
    )
)
# then transfer the original participant to out_part.identity if required
await ctx.api.sip.transfer_sip_participant(
    api.TransferSIPParticipantRequest(
        room_name="room1",
        participant_identity="current_agent_identity",
        transfer_to=out_part.identity,        # or the sip URI depending on your flow
    )
)
B) Direct transfer with trunk configured (if your LiveKit deployment allows specifying trunk on transfer) • Some LiveKit setups route outbound REFER/INVITE through the default or matching trunk for the SIP domain automatically if a matching outbound trunk exists. In that case, call:
Copy code
await ctx.api.sip.transfer_sip_participant(
    api.TransferSIPParticipantRequest(
        room_name="room1",
        participant_identity="agent_identity",
        transfer_to="<sip:zoiper-user@your-sip-domain.com>",
    )
)
• LiveKit will choose the outbound trunk matching
<http://your-sip-domain.com|your-sip-domain.com>
and send the REFER/INVITE authenticated with the trunk credentials. If your deployment requires explicitly passing
sip_trunk_id
on transfer, use pattern A. Step 5 — Verify the SIP server sees authentication • Reproduce the transfer and inspect your SIP server logs. Look for
Authorization: Digest
headers or authenticated session markers on the incoming INVITE/REFER — the request should show the auth username you configured. • If your server still returns 403: ◦ Confirm the `auth_username`/`auth_password` are correct. ◦ Verify the SIP server accepts REFER for that account. ◦ Check header policy (From/Refer-To) — server may reject based on headers even when auth is valid. Quick troubleshooting checklist • Did trunk creation return a trunk id? Use it when creating outbound participant or ensure domain matches so LiveKit selects it automatically. • Does the agent token include
SIPCall = true
? • Does your SIP domain accept Digest auth for outbound calls and allow REFER from that authenticated user? • Review SIP logs to confirm LiveKit included the Authorization header and check the exact 403 reason. If you want, I can: • Show exact CLI/SDK commands tailored to your environment (Node/Python/CLI). • Inspect a sample SIP server log entry for the incoming request (redact secrets) and point out which header/credential is failing. Which would you like next?
r
how to check logs ?
t
Great — here’s a concise guide to check the logs you need to diagnose the 403 during the cold transfer to Zoiper. I’ll include where to look in LiveKit, what to capture from your SIP domain, and how to correlate everything. 1) LiveKit logs — capture the transfer attempt • If you have the LiveKit CLI and agent: ◦ Find agent id:
lk agent list
◦ Stream logs:
lk agent logs --id <agent-id>
◦ Reproduce the transfer while streaming; note the exact timestamp. • If you run a self‑hosted LiveKit server: ◦ Tail the server logs (systemd, Docker logs, or your logging sink). Example: ▪︎ systemd:
journalctl -u livekit -f
▪︎ Docker:
docker logs -f <livekit_container>
• Enable verbose debug if needed (helps show SIP details): ◦ Start agent/server with debug or set log level to DEBUG. Then reproduce the transfer. • What to copy from LiveKit logs: ◦ The transfer API call entry (
transfer_sip_participant
) and any returned error lines. ◦ Any stack trace or RPC error showing
PermissionDenied
/
403
. ◦ Timestamps for each logged event. 2) SIP domain (your SIP server / trunk) logs — capture the incoming REFER/INVITE • Access your SIP server or provider dashboard logs (where Zoiper registers): ◦ Look for inbound requests at the same timestamp as the LiveKit log. ◦ Find the incoming REFER (or INVITE if LiveKit sends an INVITE during transfer) and the 403 response. • What to capture from SIP logs: ◦ Full SIP request text for the REFER/INVITE (start line and headers). ◦ The
Authorization
header or
WWW-Authenticate
reply (redact secrets). ◦ The exact reason or reason phrase accompanying
403 Forbidden
. ◦ Source IP of the request. • If Zoiper is the endpoint, enable SIP debug in Zoiper: ◦ Settings → Advanced → Debug → Enable SIP logging (reproduce transfer and save logs). 3) What to inspect in the SIP request/response • Presence of
Authorization: Digest ...
header — confirms LiveKit sent credentials. •
Refer-To
,
From
,
Referred-By
, and
Contact
headers — servers often enforce policies on these. • Source IP vs allowed IPs — confirm the request comes from an allowed IP if IP whitelisting is used. • Authentication challenge flow: if server returns
401
then LiveKit should retry with credentials; if it returns
403
it’s an authorization/policy rejection. 4) Correlate timestamps • Use the exact timestamp from the LiveKit log when the transfer was initiated. • Match that with the SIP server log for the incoming request and the 403 response. • This shows whether LiveKit’s request reached the SIP server and whether it included auth. 5) Quick reproduction steps to generate useful logs 1. Start LiveKit logs streaming (CLI or server tail). 2. Enable SIP logging on your SIP server and Zoiper. 3. Reproduce the cold transfer. 4. Collect: ◦ LiveKit log entries around the timestamp (copy 30s before/after). ◦ SIP server request/response text for that timestamp. ◦ Zoiper debug log (if available). 6) Share redacted snippets for help • If you want me to inspect, paste (you can redact passwords/secrets): ◦ LiveKit error lines around the transfer (with timestamp). ◦ The SIP request/response text showing the 403 (include headers but redact secrets). • I’ll point out whether LiveKit sent credentials, which header caused the rejection, or whether it’s a server policy issue. If you tell me: • How you host LiveKit (LiveKit Cloud vs self‑hosted) and • Which SIP server/provider you use, I’ll provide the exact commands or dashboard locations to retrieve logs for each system. Which environment should I tailor instructions for? Sources: Where to find your Agent Logs | LiveKit | Handling payments with PCI compliance | LiveKit | SIP Lifecycle Management Agent | LiveKit Documentation | Pass UUI data to Genesys Cloud during SIP transfers | LiveKit
r
how to enable debug logs ?
t
Thanks — you want step‑by‑step instructions to enable debug logging so you can capture the SIP transfer and diagnose the 403. Below are short, actionable steps for the likely environments (LiveKit Cloud, self‑hosted server, Agent runtime) and for SIP/Zoiper logging. Pick the section that matches your setup. A. LiveKit Cloud (deployed agent) — stream agent logs 1. Get your agent id: ◦ lk agent list 1. Stream logs in real time: ◦ lk agent logs --id <agent-id> 1. Increase verbosity (if supported by your agent deployment): ◦ If the agent was started with a log-level option, ensure it was started with DEBUG (see Agent runtime below). If you cannot change startup flags in Cloud, stream logs and ask me to help interpret the DEBUG-level entries. B. Agent runtime (local development or self‑hosted agent) — Node / Python • Python agent a. Start in dev (verbose) mode: • uv run agent.py dev --log-level=DEBUG a. Or start in production with debug: • uv run agent.py start --log-level=DEBUG • Node agent a. Dev mode: • pnpm dev --log-level=debug a. Production: • pnpm start --log-level=debug • After starting, reproduce the transfer and capture the console output. Copy logs around the transfer timestamp. C. Self‑hosted LiveKit server (systemd / Docker) • systemd: ◦ journalctl -u livekit -f ◦ To include older logs: journalctl -u livekit --since "2026-02-28 10:00" --until "2026-02-28 10:05" • Docker: ◦ docker logs -f <livekit_container> • Ensure the LiveKit process is started with LOG_LEVEL=DEBUG or equivalent CLI flag: ◦ Example env var: LIVEKIT_LOG_LEVEL=DEBUG (or use the
--log-level
flag when starting) D. SIP trunk / SIP server logs (your SIP domain) 1. Provider dashboard (if using a hosted SIP provider): open the trunk/session logs and filter by the transfer timestamp. Look for incoming REFER/INVITE and the 403 response. 2. Self‑hosted SIP server (e.g., Asterisk, FreeSWITCH, Kamailio): ◦ Enable SIP debug (example Asterisk): ▪︎ asterisk -rvvv ▪︎ sip set debug on ◦ For Kamailio/OpenSIPS, increase log level in conf and tail logs (kamailio -DD -E or check /var/log/syslog). 1. Capture raw SIP traffic for exact headers: ◦ tcpdump -i any -w sip.pcap port 5060 or port 5061 ◦ Open sip.pcap in Wireshark to inspect Refer-To, From, Authorization headers, and the 403 reason. E. Zoiper client — enable SIP debug 1. Open Zoiper settings → Advanced → Debug/SIP logging. 2. Enable SIP logging and reproduce the transfer. 3. Export the log from Zoiper (or copy the relevant timestamped entries showing the incoming REFER and any response). F. What to collect (minimum useful data) • LiveKit logs (30s before/after transfer) showing the transfer API call and any error (PermissionDenied / 403). • SIP server incoming request and 403 response (raw SIP request + headers). • Zoiper debug log for same timestamp (if available). • Exact
transfer_to
SIP URI you used (you can redact sensitive parts). G. Quick reproduction workflow 1. Start LiveKit logs streaming (A, B or C). 2. Enable SIP logging on your SIP server and Zoiper (D, E). 3. Perform the cold transfer. 4. Save the three logs and note the exact UTC timestamp. If you want, tell me: • Whether you’re using LiveKit Cloud or self‑hosted, • Which agent runtime (Node or Python), and • Which SIP server/provider you use. I’ll then give the precise command(s) tailored to your environment and explain what exact log lines to look for (e.g., Authorization header presence, Refer‑To format, and the 403 reason). Sources: Server startup modes | LiveKit Documentation | Where to find your Agent Logs | LiveKit | Streamline troubleshooting with Agent Observability | Streamline troubleshooting with Agent Observability | Preserve caller ID during SIP transfers with Twilio | LiveKit