Outbound SIP call never gets a response from destination PBX

(Note: the trunk config, IP, phone number, and domain below are sample/placeholder values for illustration — not our actual client’s details.)

Setup: Our client provided their SIP trunk/provider details, and we configured a LiveKit outbound trunk accordingly:

{
  "name": "Client PBX outbound",
  "address": "203.0.113.10",
  "transport": "SIP_TRANSPORT_TCP",
  "numbers": ["05500000"],
  "destinationCountry": "XX"
}

No auth (username/password) was provided by the client — assumed not required on their end.

Goal: Use this trunk to place an outbound call from our agent (via WarmTransferTask) to a specific extension/group on their PBX, for a warm-transfer-to-human-agent flow.

What we tried:

  1. First pass, we hardcoded the destination as a full SIP URI in sip_call_to for testing:

    sip_call_to="sip:123@client-domain.example.com",
    sip_trunk_id="<trunk-id>",
    
    

    This was rejected immediately, before any SIP traffic was sent:

    TwirpError(code=invalid_argument, message=SipCallTo should be a phone number or SIP user, not a full SIP URI, status=400)
    
    
  2. We then switched to just the bare extension instead of a full URI. is it correct to use only extension in sip_call_to:

    sip_call_to="123",
    sip_trunk_id="<trunk-id>",
    
    

    This passed validation and LiveKit did send the INVITE out — but we never got any response back from the destination at all, and it eventually failed with:

    TwirpError(code=deadline_exceeded, message=twirp error unknown: request timed out, status=408)
    

Any insights will be appreciated

Can you share a call ID for this? It starts SCL_

Below is the Call ID for the test where I specified only the SIP extension in the sip_call_to field.

CallId: SCL_uJepo2is7SHY

Thanks, as far as I can see we opened a TCP connection to your SIP server on port 5060 but the other end never responded. Perhaps some firewall issue or IP allowlist preventing the connection?

Claude did some additional investigation and suggested that your use of plain TCP on port 5060 was the culprit, and you should be providing TLS on port 5061 instead for that endpoint, SIP APIs | LiveKit Documentation

We’re referring to the LiveKit documentation, where WarmTransferTask states that the sip_call_to parameter can accept either a phone number or a SIP URI.

However, when we pass a SIP URI, the transfer fails with the following error:

TwirpError(code=invalid_argument, message=SipCallTo should be a phone number or SIP user, not a full SIP URI, status=400)


Ref: WarmTransferTask | LiveKit Documentation

The call ID shared above corresponds to a test where we specified only the extension in sip_call_to:

sip_call_to="123"

For the SIP URI test, we used the following configuration.:

ProactiveWarmTransferTask
(
    sip_call_to="sip:123@client-domain.example.com",
    sip_trunk_id=sip_trunk_id,
    chat_ctx=self.chat_ctx,
    extra_instructions=summary_instructions,
)

The error message indicates that sip_call_to should be a phone number or SIP user, rather than a full SIP URI. This appears to contradict the documentation, which mentions that a SIP URI is also a supported value.

Could you please clarify the expected format for sip_call_to when using SIP transfers?

That looks like a docs mismatch, the code will raise that error if it sees a ‘@’ in the sip_call_to param: https://github.com/livekit/sip/blob/main/pkg/sip/client.go#L193

I have only tried this with phone numbers personally but it looks like if you specify ‘123’ the invite will be sent to 123@[trunk outbound host]

However, I would strongly recommend you first test the more basic case of CreateParticipant, i.e. just making a basic outbound call to that endpoint. Since from my answer above, it looks like the endpoint is not responding.

Hi @darryncampbell

As you said, I have only tried this with phone numbers personally but it looks like if you specify ‘123’ the invite will be sent to 123@[trunk outbound host]

But from where our code will get to know where I need to redirect this call I mean host name.

As 123 is our group id that is setup in the SIP server configuration and we need to transfer our call to that group itself.

How can we achieve this? What we need to do that because normal phone call transfer is working for us using the `target_phone_number` parameter of warm transfer but we have requirement that we need to transfer the call to a SIP group id where multiple phone numbers are attached to this group and the free one will accept this call.

I do not believe we support forwarding a call to a SIP group as you describe, but @CWilson can hopefully confirm.

Hi @Rajan_kumar,

Good question — the hostname never comes from sip_call_to itself. It always comes from the trunk you dial through. The SIP service builds the INVITE target as:

sip:<sip_call_to>@<trunk address>

So sip_call_to is only the user part (a number, extension, or group ID like 123), and the trunk’s address supplies the host. Since 123 is a group ID configured on your PBX, you just need the transfer to go out via a trunk configured to point to that PBX. Two ways to do that:

Option 1: A dedicated outbound trunk pointing at your PBX

Create an outbound trunk whose address is your PBX host (plus auth credentials if it requires them), then use it for the transfer:

python

WarmTransferTask(
    sip_call_to="123",           # your group ID
    sip_trunk_id=pbx_trunk_id,   # trunk whose address is your PBX host
    chat_ctx=self.chat_ctx,
)

The INVITE goes to sip:123@your-pbx-host, and your PBX’s ring-group logic handles distributing the call to a free member of the group.

Option 2: Inline SIP connection, no saved trunk

WarmTransferTask also accepts a sip_connection parameter (api.SIPOutboundConfig), so you can specify the destination host per-transfer without creating a trunk:

python

from livekit import api

WarmTransferTask(
    sip_call_to="123",
    sip_connection=api.SIPOutboundConfig(
        hostname="client-domain.example.com",
        # transport, auth_username, auth_password if needed
    ),
    chat_ctx=self.chat_ctx,
)

This is handy if the PBX host varies per client and you don’t want to manage a trunk per destination.

One note before testing either of these: earlier in this thread, it looked like your PBX wasn’t responding to the INVITE at all. I’d echo Darryn’s suggestion to first verify a plain CreateSIPParticipant call to 123 through the PBX-pointed trunk connects successfully — make sure your PBX accepts traffic from LiveKit’s SIP IP ranges and that the transport/port match its configuration. Once that basic call works, the warm transfer will behave the same way.

Hey @CWilson,

Here’s our understanding of the call transfer flow. To transfer a call to a human agent, we need two things:

  1. Origination (where the call originates from)

  2. Target (where the call should be transferred)

We requested these details from our SIP provider, and they shared the following:

Outbound SIP connection details

  • SIP server/domain: some IP address like 22.33.333.222

  • Transport protocol: TCP

  • Phone number associated with the connection: xxxxxxxxx

  • Authentication credentials: Not required

SIP destination for call transfers

  • The SIP URI of the group.

  • For example, if the transfer target is group 998, the SIP URI would be:
    sip:998@example.site.com

Based on these details, we created the outbound trunk successfully. However, we’re not sure what value should be provided in sip_call_to for the WarmTransferTask.

Could you suggest how the WarmTransferTask should be configured in this scenario, where the transfer target is a SIP group URI rather than a phone number?

It looks like you will just treat it like any other number for the provider.

Can you send me example like this if possible? according to my data that I have.

I am not understanding what your issue is. Do you have transfer to regular number working? What is not working with the group transfer? Is it not exactly the same as transfer to number?

Do you have a call ID where it did not work?

Hi @CWilson

Hi, we don’t have a regular phone number. According to the documentation, LiveKit supports two ways to specify the target:

  1. A regular phone number
  2. sip_call_to

We want to integrate the second option (sip_call_to), and our provider has shared the required details above for that.

We need some guidance on how to configure and set this up correctly.

Have you tried it? What is not working for you?

import os

from livekit.agents.beta.workflows import WarmTransferTask
from livekit.protocol.sip import SIPOutboundConfig

result = await WarmTransferTask(
    sip_call_to=<manager-phone-number>,   # Manager's phone number
    sip_connection=SIPOutboundConfig(      # Inline trunk configuration
        hostname=os.getenv("SIP_TRUNK_HOSTNAME"),
        auth_username=os.getenv("SIP_AUTH_USERNAME"),
        auth_password=os.getenv("SIP_AUTH_PASSWORD"),
    ),
    chat_ctx=self.chat_ctx,               # Conversation history
    dtmf="wwww1234#",                     # Dial extension 1234 after ~2s pause
    ringing_timeout=30.0,                 # Give up after 30s if no answer
)

Do you have a PCAP of a failure?

Hi,

We have successfully tested direct call transfer to a phone number using the following configuration:

task = ProactiveWarmTransferTask(
    target_phone_number=supervisor_phone,
    sip_trunk_id=sip_trunk_id,
    hold_audio=AudioConfig(BuiltinAudioClip.HOLD_MUSIC, volume=0.5),
    chat_ctx=self.session.history,
    extra_instructions=summary_instructions,
    llm=stateless_llm
)

Here:

  • supervisor_phone is the destination phone number (for example, +9198XXXXXXXX).

  • sip_trunk_id is the LiveKit outbound SIP trunk ID that we created using our SIP provider’s outbound trunk details.

This setup works as expected, and we’ve verified that the call transfer is successful.

Our challenge is that our client does not have a dedicated destination phone number. Instead, they want us to transfer calls to a SIP group, which is associated with multiple phone numbers. The expectation is that when a call is transferred to this SIP group, whichever agent is available should receive the call.

We requested the required details from the SIP provider, and they shared the following:

Outbound SIP connection details

  • SIP server/domain: 22.33.333.222 (example)

  • Transport protocol: TCP

  • Phone number associated with the connection: xxxxxxxxx

  • Authentication credentials: Not required

SIP destination for call transfers

  • The SIP URI of the group.

For example, if the target group is 998, the SIP URI would be:

sip:998@example.site.com

Our questions are:

  1. Does LiveKit support transferring calls directly to a SIP URI (such as sip:998@example.site.com) instead of a phone number?

  2. If yes, how should we configure ProactiveWarmTransferTask (or any other supported API) to transfer the call to the SIP group instead of using target_phone_number?

  3. If there is a recommended approach or configuration for this use case, could you please share it?

We’d appreciate your guidance as soon as possible. If it’s easier to explain over a quick call, we’d be happy to join one at your convenience.

Thanks!

Guys, can you suggest or provide a solution for the above problem? @CWilson @darryncampbell

I thought CWilson had already answered these questions here: Outbound SIP call never gets a response from destination PBX - #9 by CWilson

Have you successfully tested a direct connection to your endpoint? Before assuming there is an issue with transfer, I would like to be sure that the endpoint is reachable, this is the crux of what CWilson is saying here:

One note before testing either of these: earlier in this thread, it looked like your PBX wasn’t responding to the INVITE at all. I’d echo Darryn’s suggestion to first verify a plain CreateSIPParticipant call to 123 through the PBX-pointed trunk connects successfully — make sure your PBX accepts traffic from LiveKit’s SIP IP ranges and that the transport/port match its configuration. Once that basic call works, the warm transfer will behave the same way.

Hi,

We tested a direct CreateSIPParticipant call to 998 as suggested, but it failed with the following error:

error: "transport<TCP> dial err=dial tcp :9739->X.X.X.X:5060: connect: connection timed out"
mediaEncryption: "SIP_MEDIA_ENCRYPT_DISABLE"

Call ID: SCL_dJRmvdzYnHjc

We’re checking with our provider to understand why the PBX isn’t responding to the INVITE.

We also wanted to clarify the expected usage of sip_call_to. Our client provided:

From

  • SIP server/domain: X.X.X.X
  • Transport: TCP
  • Phone number: 012345678

To

  • SIP URI: sip:998@example.site.com

We initially passed the full SIP URI to sip_call_to based on the documentation, but you mentioned that full SIP URIs are not currently supported.

If that’s the case, how should we specify a destination like sip:998@example.site.com? Should we pass only the SIP user (998) and let the configured SIP trunk handle the routing, or is there another supported approach?

Regarding:

how should we specify a destination like sip:998@example.site.com? Should we pass only the SIP user (998) and let the configured SIP trunk handle the routing, or is there another supported approach?

CWilson knows more about SIP than myself (or most people :slight_smile: ) and provided the following which I believes answers this:

Regarding:

We tested a direct CreateSIPParticipant call to 998 as suggested, but it failed with the following error:

error: "transport<TCP> dial err=dial tcp :9739->X.X.X.X:5060: connect: connection timed out"
mediaEncryption: "SIP_MEDIA_ENCRYPT_DISABLE"

Is this the same issue as we spoke about earlier in this thread, here? Outbound SIP call never gets a response from destination PBX - #4 by darryncampbell , that looked like a port / encryption config issue.