SIP Transfers
In a SIP transfer, the party transferring the call doesn't dial the transfer destination. Instead, it sends a REFER asking the SIP peer on the other end of the call (the transferee) to dial it, and that new call replaces the current one. This guide explains how that works when a Guava agent transfers a call, and what it means for your transfer destinations.
A basic call setup
Let's start by reviewing a basic call setup. Say you have an ITSP (like Twilio Elastic SIP) that converts inbound PSTN calls to SIP calls. These calls are forwarded to Guava's SIP trunk and land on a specific Guava agent.
From Guava's point of view, the ITSP is the SIP peer. Every SIP message for this call is exchanged between Guava and the ITSP.
What happens when you transfer
When the agent calls call.transfer(), Guava sends a SIP REFER to the ITSP. The Refer-To header contains your transfer destination.
call.transfer("sip:support@pbx.example.com", "Let the caller know you're transferring them to support.")Guava ──REFER (Refer-To: sip:support@pbx.example.com)──▶ ITSP
ITSP ──INVITE──▶ sip:support@pbx.example.com
ITSP bridges the caller to the new call, and Guava leaves the callPhone numbers work the same way. A destination like +18005550199 becomes a tel: URI in the Refer-To header, and the ITSP dials the number.
# Sends REFER with Refer-To: tel:+18005550199
call.transfer("+18005550199")Choosing a destination
Since the ITSP places the new call, pick destinations that the ITSP can reach:
# Works: a public SIP URI on your PBX that the ITSP can reach.
call.transfer("sip:support@pbx.example.com")
# Works: another Guava agent, since the ITSP can already reach Guava's SIP trunk.
call.transfer("sip:guavasip-xxx@sip.goguava.ai")
# Works if the ITSP allows PSTN transfers.
call.transfer("+18005550199")The ITSP also decides whether to accept the REFER at all. Some ITSPs require REFER to be enabled, or limit which destinations they will dial. Please see your ITSP provider's docs for more information.
Sending information to the transfer destination
The transfer URI can carry extra data for the transfer destination, such as the caller's account ID or why they're calling. There are two ways to attach it, and which one to use depends on the system you're transferring to.
SipUri helper to build your transfer destination URI. It automatically handles escaping in header and URI params. You can also build the URI string yourself.URI parameters are appended to the URI after a ;. The ITSP dials the URI with the parameters included, so the destination can read them from the Request-URI of the incoming INVITE.
from guava.sip import SipUri
# INVITE sip:support@pbx.example.com;queue=billing;account=12345
call.transfer(SipUri(
user="support",
host="pbx.example.com",
uri_params={"queue": "billing", "account": "12345"},
))Header parameters are appended after a ?, with multiple headers joined by &. The ITSP adds each one as a SIP header on the INVITE it sends to the destination. Use the X- prefix for custom headers.
from guava.sip import SipUri
# INVITE sip:support@pbx.example.com
# X-Account-Id: 12345
# X-Reason: <reason_for_calling>
call.transfer(SipUri(
user="support",
host="pbx.example.com",
headers={
"X-Account-Id": "12345",
"X-Reason": call.get_field("reason_for_calling"),
},
))These options only apply to sip: destinations, not phone numbers.
X- headers. Check your ITSP's docs before relying on either.User-to-User (UUI) information
Many contact center platforms and PBXs expect call data in the standard User-to-User header (RFC 7433) rather than in custom X- headers. You can send it as a header parameter like any other header. The value is usually hex-encoded, followed by an encoding=hex parameter.
from guava.sip import SipUri
uui = "account=12345".encode().hex()
# INVITE sip:support@pbx.example.com
# User-to-User: 6163636f756e743d3132333435;encoding=hex
call.transfer(SipUri(
user="support",
host="pbx.example.com",
headers={"User-to-User": f"{uui};encoding=hex"},
))Include ;encoding=hex in the header value. SipUri URL-encodes it along with the rest of the value, since ; and = aren't allowed unescaped in a URI header value.
The format of the payload itself is up to the receiving system, so check what it expects. Some systems also limit the payload size, so keep it short.
Questions? hi@goguava.ai
# Works: a public SIP URI on your PBX that the ITSP can reach.
call.transfer("sip:support@pbx.example.com")
# Works: another Guava agent, since the ITSP can already reach Guava's SIP trunk.
call.transfer("sip:guavasip-xxx@sip.goguava.ai")
# Works if the ITSP allows PSTN transfers.
call.transfer("+18005550199")from guava.sip import SipUri
# INVITE sip:support@pbx.example.com;queue=billing;account=12345
call.transfer(SipUri(
user="support",
host="pbx.example.com",
uri_params={"queue": "billing", "account": "12345"},
))from guava.sip import SipUri
# INVITE sip:support@pbx.example.com
# X-Account-Id: 12345
# X-Reason: <reason_for_calling>
call.transfer(SipUri(
user="support",
host="pbx.example.com",
headers={
"X-Account-Id": "12345",
"X-Reason": call.get_field("reason_for_calling"),
},
))from guava.sip import SipUri
uui = "account=12345".encode().hex()
# INVITE sip:support@pbx.example.com
# User-to-User: 6163636f756e743d3132333435;encoding=hex
call.transfer(SipUri(
user="support",
host="pbx.example.com",
headers={"User-to-User": f"{uui};encoding=hex"},
))