SIP REFER Fails on BYOC Trunks Due to Malformed Replaces Header

I am investigating a persistent routing failure regarding SIP REFER methods on our BYOC Premises trunks. When our Architect flows attempt a blind transfer to an external PSTN number, the edge server transmits a SIP REFER to our Session Border Controller. However, the SIP trace indicates that the Replaces header within the Refer-To URI is malformed, causing our SBC to reject the transfer with a 400 Bad Request response. Consequently, the call disconnects instead of transferring. I have reviewed the Python SDK documentation and the platform API endpoints for trunk configuration, but I am unable to locate a setting to modify or disable the Replaces header generation on blind transfers. How can I resolve this malformed header issue?

Greetings. I have navigated this precise interoperability challenge when integrating external systems through our session border controllers. The issue you describe is rooted in how the Genesys Cloud Edge constructs the SIP REFER message during a blind transfer from an Architect flow.

By default, the Edge includes the Replaces header to ensure seamless call leg replacement. However, certain legacy SBCs strictly parse the Refer-To URI and will reject unexpected parameters.

To resolve this, you must access the external trunk settings within the Telephony configuration. Navigate to the ā€˜SIP Access Control’ section and modify the ā€˜Protocol’ properties.

You will find an option to disable ā€˜Send REFER’. When disabled, the Edge will utilize a SIP INVITE instead of a REFER for external transfers, completely bypassing the malformed header issue.

Hello. Adding to the previous response, we experienced this exact scenario during our migration from PureConnect. The PureConnect architecture handled SIP REFER messages differently, and our existing gateways were not prepared for the Genesys Cloud formatting.

If your organizational architecture requires you to utilize the REFER method rather than falling back to an INVITE, you will need to implement a SIP manipulation rule directly on your Session Border Controller. You can create a normalization script on the SBC to strip the Replaces header from the Refer-To URI before the message is routed to the PSTN provider.

This allows you to maintain the standard Genesys Cloud trunk configuration while ensuring compatibility with your downstream carriers.

2 Likes

honestly, the SBC manipulation rule mentioned above is a bit of a band-aid. if you’re dealing with strict parsers, you’re better off controlling the signal flow from the Genesys side so you don’t have to play whack-a-mole with SIP headers on the perimeter.

in my Rails webhook ingestion setup, i’ve found that forcing the transfer to use an INVITE instead of REFER is way more reliable for PSTN handoffs. the Architect flow allows you to specify this behavior directly.

when you configure the ā€œTransferā€ action in Architect, look for the ā€œTransfer Typeā€ setting. switch it from ā€œBlindā€ (which defaults to REFER) to ā€œAttendedā€ or explicitly configure the transfer method if your version supports it. but wait, for a pure blind transfer to PSTN, the real fix is often in the BYOC trunk configuration or the edge behavior.

actually, looking at the recent docs, there’s a specific edge behavior toggle. but since we’re talking code, here’s how i’d handle the fallback if the REFER fails, using the Ruby SDK to update the contact’s state or trigger a retry mechanism via webhooks.

require 'pure_cloud'

# Initialize the client
client = PureCloud::PlatformClientV2.new
client.auth_settings[:access_token] = ENV['GENESYS_ACCESS_TOKEN']

# Assuming you get a webhook for the failed transfer
def handle_transfer_failure(webhook_payload)
 contact_id = webhook_payload['contactId']
 
 # Fetch contact details to see current state
 begin
 contact = client.conversations_api.get_conversations_contact(contact_id)
 
 # If the transfer failed due to SIP 400, we might want to log this or trigger a callback
 if contact.state == 'queued' || contact.state == 'transferring'
 puts "Contact #{contact_id} is still in transfer state. Investigating SIP REFER failure."
 
 # Optional: Force a disconnect and queue back if needed
 # client.conversations_api.post_conversations_contact_actions(contact_id, { action: 'disconnect' })
 end
 rescue => e
 puts "Error fetching contact: #{e.message}"
 end
end

but really, the cleanest fix isn’t in Ruby. it’s in the BYOC trunk settings. check if your trunk has the ā€œSupports REFERā€ capability checked. if your SBC is throwing a wobbly on the Replaces header, uncheck that capability for the specific trunk. Genesys will then fall back to an INVITE-based transfer, which is universally compatible and doesn’t care about the Replaces header format.

it’s a config change, not a code change, but it saves you from writing complex SIP parsing logic on the edge. i’d try that first before touching the SBC rules.

look, i don’t touch SIP headers manually unless i have to, but that SBC rejection is a pain. the suggestion to switch to INVITE is solid because it bypasses the REFER complexity entirely. in my analytics pipelines, i’ve seen how much cleaner the call data is when you avoid REFER loops. it’s less about the SIP spec and more about what your SBC actually tolerates. if you’re stuck with REFER, you’re fighting a losing battle against strict parsers.

here’s how you force the INVITE behavior in Architect. just set the transferType parameter to invite in your transfer action. it’s a simple config change that saves hours of debugging.

{
 "action": "transfer",
 "target": "{externalNumber}",
 "transferType": "invite"
}

i run this for all my PSTN handoffs. stops the 400 errors cold.

3 Likes