Feb 25 08:53:20 ip-172-31-23-35.ec2.internal pytho...
# ask-ai
w
Feb 25 085320 ip-172-31-23-35.ec2.internal python3[1964113]: {"message": "Transferring call to +918956521667", "level": "INFO", "name": "outbound-caller", "pid": 1964126, "job_id": "AJ_FD4fS8rmBV6o", "room_id": "RM_Hv3NrxPRKy6R", "timestamp": "2026-02-25T085320.089927+00:00"} Feb 25 085320 ip-172-31-23-35.ec2.internal python3[1964113]: {"message": "transfer_sip_participant failed\nTraceback (most recent call last):\n File \"/home/ec2-user/livekit-agent/agent.py\", line 1112, in transfer_call\n await job_ctx.api.sip.transfer_sip_participant(\n File \"/home/ec2-user/livekit-agent/venv/lib64/python3.11/site-packages/livekit/api/sip_service.py\", line 815, in transfer_sip_participant\n return await self._client.request(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ec2-user/livekit-agent/venv/lib64/python3.11/site-packages/livekit/api/twirp_client.py\", line 129, in request\n raise TwirpError(\nlivekit.api.twirp_client.TwirpError: TwirpError(code=permission_denied, message=twirp error unknown: sip status: 403: Forbidden, status=403, metadata={'error_details': 'CAcSGnNpcCBzdGF0dXM6IDQwMzogRm9yYmlkZGVuGjcKJXR5cGUuZ29vZ2xlYXBpcy5jb20vbGl2ZWtpdC5TSVBTdGF0dXMSDgiTAxIJRm9yYmlkZGVu', 'sip_status': 'Forbidden', 'sip_status_code': '403'})", "level": "ERROR", "name": "outbound-caller", "exc_info": "Traceback (most recent call last):\n File \"/home/ec2-user/livekit-agent/agent.py\", line 1112, in transfer_call\n await job_ctx.api.sip.transfer_sip_participant(\n File \"/home/ec2-user/livekit-agent/venv/lib64/python3.11/site-packages/livekit/api/sip_service.py\", line 815, in transfer_sip_participant\n return await self._client.request(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ec2-user/livekit-agent/venv/lib64/python3.11/site-packages/livekit/api/twirp_client.py\", line 129, in request\n raise TwirpError(\nlivekit.api.twirp_client.TwirpError: TwirpError(code=permission_denied, message=twirp error unknown: sip status: 403: Forbidden, status=403, metadata={'error_details': 'CAcSGnNpcCBzdGF0dXM6IDQwMzogRm9yYmlkZGVuGjcKJXR5cGUuZ29vZ2xlYXBpcy5jb20vbGl2ZWtpdC5TSVBTdGF0dXMSDgiTAxIJRm9yYmlkZGVu', 'sip_status': 'Forbidden', 'sip_status_code': '403'})", "pid": 1964126, "job_id": "AJ_FD4fS8rmBV6o", "room_id": "RM_Hv3NrxPRKy6R", "timestamp": "2026-02-25T085320.109155+00:00"}
t
Thanks for reaching out, Sujata. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I can see you're asking about the failed SIP transfer in your logs (the
transfer_sip_participant
call returning a TwirpError with SIP status 403 Forbidden). I’m sorry this is blocking your transfer flow — I’ll walk through the likely cause and what to check next. What the error means • The 403 Forbidden comes from your SIP provider rejecting the transfer request (the error metadata shows
sip_status: "Forbidden"
). LiveKit is forwarding the transfer request; the provider is denying it. • A 403 on transfers normally indicates the provider or trunk is not permitting SIP REFER/call transfer operations. What to check and how to fix it 1. Verify your SIP trunk supports and has SIP REFER / call transfer enabled. ◦ If you use Twilio, ensure Call Transfer (SIP REFER) and PSTN Transfer are enabled in the trunk settings. ◦ For other providers, look for equivalent settings (SIP REFER, transfer/REFER permitted, PSTN transfer enabled). 1. Confirm any trunk restrictions or ACLs aren’t blocking the target: ◦ Ensure the outbound trunk allows transfers to the destination number/URI you’re trying to reach. ◦ Check if there are any IP, number, or domain restrictions that would cause the provider to return 403. 1. Reproduce a simple transfer after changing settings: ◦ Make a basic transfer request to a known-working destination to confirm the provider now accepts REFER requests. 1. Collect helpful diagnostics if it still fails: ◦ Provider SIP response (full SIP headers if available). ◦ The trunk/provider name and exact transfer target (obscure the phone number if sensitive). ◦ A minimal sequence of steps or the API call you used (you already included
transfer_sip_participant
failure in the logs — include the request payload if possible). If you’d like, I can help next by: • Walking through the Twilio trunk settings if that’s your provider. • Reviewing any transfer request payloads or logs you can share (mask any sensitive data). • Suggesting a minimal test transfer call sequence to validate configuration. Which provider are you using for the SIP trunk, and can you confirm whether SIP REFER / Call Transfer is currently enabled in the trunk settings? Sources: Agents telephony integration | LiveKit Documentation
w
$response2 = Http::withBasicAuth($accountSid, $authToken) ->asForm()->post("https://trunking.twilio.com/v1/Trunks/$trunkSid", [ 'DomainName' => $domainName, 'TransferMode' => 'enable-all', ]); i use this then also got error
t
Thanks — that’s helpful. You’ve shown the API call setting
TransferMode
to
enable-all
, and I can see you still get a 403 on the transfer. I’m sorry this is still blocking you — here’s what to check next and why. What’s happening • Setting
TransferMode = enable-all
allows SIP REFER within the trunk, but it does not automatically allow outbound transfers to the PSTN (phone numbers). • Twilio requires an additional setting, Enable PSTN Transfer, for transfers to external phone numbers to succeed. Without that, Twilio will reject the REFER with a 403 and return the SIP Forbidden status you see in the logs. What to do next 1. Enable PSTN transfer for the Twilio trunk ◦ Open the Twilio console for the trunk and enable the PSTN transfer option (this is separate from
TransferMode
). 1. Confirm your API request succeeded ◦ Verify the HTTP response from your POST to
<https://trunking.twilio.com/v1/Trunks/$trunkSid>
returned a 200/201 and shows the updated settings. ◦ If the API returned an error or insufficient-permission response, the change may not have been applied. 1. Check credentials and permissions ◦ Ensure the
$accountSid
/
$authToken
used have permission to change trunk settings and that the account isn’t restricted from enabling PSTN features. 1. Verify transfer target format ◦ For PSTN transfers use
tel:+E.164
(tel: URI) as the transfer target when appropriate. For SIP-to-SIP transfers use a
sip:
URI. 1. Re-test a simple transfer ◦ After enabling PSTN transfer, attempt a basic transfer to a known working phone number to confirm success. If it still fails, please share these items (mask sensitive data): • The HTTP response body and status code from your trunk update call. • The trunk Sid (or confirm it) and whether you changed settings via API or console. • The exact transfer request payload or the
transfer_to
value type (
tel:
vs
sip:
). • Any Twilio console logs or carrier SIP response details you can copy. I can walk you through the Twilio console steps or help interpret the API response you got — which would you prefer? Sources: Preserve caller ID during SIP transfers with Twilio | LiveKit | Pass UUI data to Genesys Cloud during SIP transfers | LiveKit | Handling payments with PCI compliance | LiveKit
w
Enable PSTN transfer for the Twilio trunk ◦ Open the Twilio console for the trunk and enable the PSTN transfer option (this is separate from
TransferMode
) how enable this from api
t
Thanks — I see you want to enable PSTN transfer on your Twilio SIP trunk via API (not the console). You previously set
TransferMode
but still got a 403, which happens when PSTN transfer support isn’t enabled. Here’s exactly how to enable it via Twilio’s REST API. What to set • You must set the trunk parameter
EnableOutboundPstnTraffic=true
(this is separate from
TransferMode
). API endpoint and method • POST to: https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Trunking/Trunks/{TrunkSid}.json cURL example 1. Replace {AccountSid}, {AuthToken}, and {TrunkSid} and run:
Copy code
curl -X POST "<https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Trunking/Trunks/{TrunkSid}.json>" \
-u "{AccountSid}:{AuthToken}" \
--data-urlencode "EnableOutboundPstnTraffic=true"
Twilio CLI example
Copy code
twilio api:trunking:v1:trunks:update --sid <TrunkSid> --enable-outbound-pstn-traffic
Notes and verification • You still should keep
TransferMode=enable-all
(or the mode you need) for SIP REFER behavior. Both settings together allow transfers to PSTN numbers. • Confirm the API response returns HTTP 200 and the trunk resource shows
EnableOutboundPstnTraffic
enabled. • Ensure the credentials you use have permission to update trunk settings. • For outbound PSTN transfers, use the appropriate transfer target format (e.g.,
tel:+E.164
when transferring to PSTN). • After updating, re-test the LiveKit
transfer_sip_participant
call. The 403 should be resolved if the trunk accepts PSTN transfers. If you’d like, paste the HTTP response (status code and body) you get after your update request — mask any secrets — and I’ll review it to confirm the setting was applied or help troubleshoot why it wasn’t. Sources: Preserve caller ID during SIP transfers with Twilio | LiveKit
w
$response2 = Http::withBasicAuth($accountSid, $authToken) ->asForm()->post("https://trunking.twilio.com/v1/Trunks/$trunkSid", [ 'DomainName' => $domainName, 'TransferMode' => 'enable-all', 'EnableOutboundPstnTraffic' => true, ]); this is correct?
t
Thanks — I see the code you’re using to update the trunk. You’re almost correct, but a few adjustments will make it reliably work with Twilio’s API. Quick answer • Use the Twilio Accounts Trunking endpoint (the 2010-04-01 endpoint) to set
EnableOutboundPstnTraffic
. The trunking.v1 endpoint you posted can work for some operations, but the documented update endpoint is: https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Trunking/Trunks/{TrunkSid}.json What to change in your request 1. Endpoint ◦ POST to: https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Trunking/Trunks/{TrunkSid}.json 1. Parameter names and values ◦
TransferMode
should be a string such as
enable-all
— that’s fine. ◦
EnableOutboundPstnTraffic
must be sent as a form field with value
true
(string) or
1
depending on your HTTP client. Passing a boolean may or may not be encoded correctly by the client — send it as
"true"
to be safe. Example (Laravel HTTP client) 1. Using the 2010 API endpoint and ensuring
EnableOutboundPstnTraffic
is sent as a string:
Copy code
$response2 = Http::withBasicAuth($accountSid, $authToken)
    ->asForm()
    ->post("<https://api.twilio.com/2010-04-01/Accounts/{$accountSid}/Trunking/Trunks/{$trunkSid}.json>", [
        'DomainName' => $domainName,
        'TransferMode' => 'enable-all',
        'EnableOutboundPstnTraffic' => 'true',
    ]);
What to verify after the request • Check the HTTP status code is 200 (or 201) and the JSON response shows
"enable_outbound_pstn_traffic": true
(or equivalent property) in the trunk resource. • If you get a permissions error or non-2xx response, share the HTTP status and response body (mask secrets) and I’ll help interpret. Next test • Once the trunk shows PSTN transfer enabled, retry the LiveKit
transfer_sip_participant
to a PSTN number (use
tel:+E.164
). If you still see a 403, share the Twilio response body or SIP headers returned with the 403 and I’ll help dig into it. Would you like me to help examine the HTTP response you get from your current call?