SIP Cold Transfer fails with SIP 603
Hello!
I've been using LiveKit (Cloud) for a while and been enjoying it so far (thank you for building and maintaining it everyday).
I'm building a voice agent using LiveKit Agents and relying on LiveKit SIP for inbound and outbound telephony alongside Twilio for (elastic) trunking and a local VoIP company that provides me with local (regional) phone numbers.
I have set up inbound and outbound trunks as well as the appropriate dispatch rules to handle incoming SIP traffic.
For reference, here are my (redacted) trunks and dispatch rules:
> lk sip outbound list
Using default project [<redacted>]
┌─────────────────┬──────────────────────────┬────────────────────────────┬───────────┬──────────────┬────────────────┬──────────┐
│ SipTrunkID │ Name │ Address │ Transport │ Numbers │ Authentication │ Metadata │
├─────────────────┼──────────────────────────┼────────────────────────────┼───────────┼──────────────┼────────────────┼──────────┤
│ ST_u<redacted> │ Outbound Trunk │ <redacted>.pstn.twilio.com │ UDP │ +34123456789 │ │ │
└─────────────────┴──────────────────────────┴────────────────────────────┴───────────┴──────────────┴────────────────┴──────────┘
> lk sip inbound list
Using default project [<redacted>]
┌─────────────────┬─────────────────────────┬───────────────────────────┬──────────────────┬────────────────┬────────────────┬──────────┐
│ SipTrunkID │ Name │ Numbers │ AllowedAddresses │ AllowedNumbers │ Authentication │ Metadata │
├─────────────────┼─────────────────────────┼───────────────────────────┼──────────────────┼────────────────┼────────────────┼──────────┤
│ ST_mn<redacted> │ Inbound Trunk. │ +34123456789,+34123456789 │ │ │ │ │
└─────────────────┴─────────────────────────┴───────────────────────────┴──────────────────┴────────────────┴────────────────┴──────────┘
> lk sip dispatch list
Using default project [<redacted>]
┌───────────────────┬─────────────────────────┬───────────┬─────────────────────┬────────────────────────┬─────┬───────────┬──────────┐
│ SipDispatchRuleID │ Name │ SipTrunks │ Type │ RoomName │ Pin │ HidePhone │ Metadata │
├───────────────────┼─────────────────────────┼───────────┼─────────────────────┼────────────────────────┼─────┼───────────┼──────────┤
│ SDR_QG<redacted> │ Dispatch Rule. │ <any> │ Individual (Caller) │ call_<caller>_<random> │ │ false │ │
└───────────────────┴─────────────────────────┴───────────┴─────────────────────┴────────────────────────┴─────┴───────────┴──────────┘
My agent needs to transfer calls to a human operator using one of its tools. Originally, the implementation used the transfer_sip_participant method like this:
await sip_service.transfer_sip_participant(
TransferSIPParticipantRequest(
participant_identity=self._participant.identity, #Provided elsewhere
room_name=self._room.name, #Provided elsewhere
transfer_to="sip:+34<called_number>@<redacted>.pstn.twilio.es;transport=udp"
)
)
Unfortunately, during testing I discovered that when the agent calls this tool, it fails with
livekit.api.twirp_client.TwirpError: ('internal', 'SIP REFER failed with code 603')
Further debugging revealed that, apparently, the SIP request never reaches Twilio, as there are no logs regarding failed connection attempts (or any logs at all, for the matter).
I've also tried using other transfer_to values (as per the docs), none of them work.
-
transfer_to="sip:+34123456789@<redacted>.pstn.twilio.com;transport=udp" -
transfer_to="tel:+34<called_number>"
My understanding here is that this must be a bug or me somehow messing up the outbound rules.
In the meantime, I had to implement a temporary call transfer method using outbound calling and that works without any issue.
The implementation looks like
sip_participant = await sip_service.create_sip_participant(
CreateSIPParticipantRequest(
sip_trunk_id="ST_u<redacted>",
sip_call_to="+34<called_number>",
participant_identity=f"sip_{nanoid.generate()}",
room_name=self._room.name,
participant_name="SIP",
play_dialtone=True
)
)
# Remove the agent
await room_service.remove_participant(
RoomParticipantIdentity(
room=self._room.name,
identity=self._room.local_participant.identity
)
)
Which, again, works fine and the called number receives a call from the number configured in my outbound trunk (which is also properly authorized in Twilio as a caller ID).
My issue with this method is that it's messy and the called number sees the outbound trunk's number as the caller id (ideally, it should see the original caller's caller ID)
Let me know if you need more details.
Best Regards, Ramón
Hello again! I haven't been able to fix this yet, but I'm willing to put in the work. I would appreciate some pointers because I'm not very proficient in golang and it's quite hard for me to fully grasp the more complex bits.
Hello @rmonvfer ! Could you please provide more details on the recent failed transfers? You could join our Slack and share your project ID there, so we can debug further.
Thanks @dennwc, I'll definitely join the Slack!
I get a deadline_exceeded error
have you solved that problem to transfer the call ?
any one solved this issue? me facing the same
I have the same problem, were you able to transfer to different trunkIDs?