WebRTC softphone media handshake failing with SDP negotiation error on hybrid CXone route

I’m hitting a wall with the custom softphone UI whenever routing targets a CXone-hosted queue. Pure Genesys Cloud destinations work without issue. We’re running JS SDK 3.24.3 inside a React frontend. The WebSocket connection holds steady, signaling completes, but the browser console immediately dumps the SDP error right after createConnection resolves.

Network trace confirms the initial ICE candidates exchange, then the server pushes back a 480 Busy Here with a malformed SDP answer. I verified the STUN servers listed in the ORG CONFIG. They respond fine from Australia/Sydney. I attempted forcing plan-b SDP semantics in the SDK CONFIG since the CXone side appears to be sending unified-plan offers. It’s doing jack all.

Here is the setup and what has been tested:

  • Genesys Cloud Org: EU10
  • CXone Org: US1 (hybrid routing via SIP Interop connector)
  • JS SDK: 3.24.3 (loaded via CDN)
  • Browser: Chrome 120, Edge 119
  • SDK CONFIG overrides: stunServers, turnServers, sdpSemantics: "plan-b", audioCodecs: ["opus", "g722", "pcmu"]
  • Verified SIP TRUNK CODECS match between platforms
  • Ran Wireshark on the media stream, seeing repeated STUN binding requests timing out after 15s
  • Swapped to a fresh SDK instance, cleared browser cache, disabled all extensions

The error payload from the signaling channel looks like this:

{
 "code": "sdp_negotiation_failed",
 "message": "Remote SDP answer contains unsupported codec payload type 98",
 "details": "ICE state: failed"
}

Payload type 98 maps to G.722 in the offer, but the answer strips it out. Ran Wireshark again just to be sure. The SDK isn’t falling back to PCMU like it should. The ARCHITECT FLOW for the hybrid route has Enable WebRTC toggled on. Destination points straight to cxone_queue_id. Logs on the CXone side show the call arriving but immediately rejecting the media parameters. Mic stays hot on the Genesys side, audio never bridges.

The fallback logic just won’t trigger on this build. What specific API INTEGRATION parameter or ROUTING SETTING am I missing to force proper codec negotiation across the hybrid connector? I’ve exhausted the standard SDK CONFIG overrides and need a definitive path forward.

From a Salesforce admin perspective, when SDP negotiation breaks on hybrid routes, it’s almost always a media path mismatch rather than a pure signaling failure. As we’ve discussed in several recent community threads, once Genesys Cloud hands off to a CXone-hosted queue, the underlying media server swaps out mid-session. It’s a pretty common gotcha. If you’re running custom React softphones on older SDK versions, they tend to cache that initial SDP format and outright reject the answer because the codec negotiation shifts based on the CXone media region.

Your first step should be to validate the CTI adapter configuration. The Genesys Cloud for Salesforce managed package (v8.4+) actually ships with a built-in fallback for hybrid routing that forces the softphone to renegotiate codecs automatically. If you’re spinning up a custom UI alongside standard click-to-dial and screen pop flows, you’ll want to hook into the state change event and reset the peer connection before the 480 error fires. I’d strongly advise against relying on the default ICE timeout here. You can handle it like this:

softphone.on('mediaStateChange', (state) => {
 if (state === 'connecting' && queueType === 'hybrid') {
  pc.setLocalDescription(null);
  pc.restartIce();
 }
});

On the Salesforce side, call logging will still function correctly even if the softphone connection drops. To keep your agent workflow intact while the media handshake sorts itself out, route the hybrid queue through a Data Action that pushes the callId and target to a custom object. You can then trigger a screen pop via the CTI adapter. This mirrors how we handle Apex-driven call logging hooks, ensuring the agent still gets the context they need without breaking the UI.

Make sure the hybrid route in Genesys isn’t set to earlyMedia. That flag breaks custom WebRTC implementations pretty hard, especially when you’re trying to maintain clean screen pop triggers. Toggle it off in the routing script. Also, verify that your SFDC org isn’t intercepting the WebSocket with a custom security policy. I’ve seen cases where session replay or compliance recorders mess with the ICE candidates, which throws off the CTI adapter’s ability to maintain the session.

Finally, bump the SDK to 3.25.1. The patch fixes a known SDP answer parser bug that trips up on CXone media servers. It’s a straightforward update that aligns with the current GC for Salesforce package recommendations.