Guest API WebSocket handshake 401 when using custom OAuth token in Kotlin Android app

We’re building a custom chat UI in our Android app using Kotlin and the Genesys Cloud Guest API. We want to avoid the default widget and handle the WebSocket connection manually. The docs say to use wss://webchat-eu.genesyscloud.com/ws?oauth_token=... or the equivalent US endpoint.

I’ve got the OAuth flow working fine for other APIs. I’m using com.genesyscloud:genesys-cloud-sdk to get an access token. The token is valid because I can call /api/v2/oauth/tokeninfo with it and get a 200 OK. But when I try to open the WebSocket connection with that token as a query param, I get a 401 Unauthorized immediately on the handshake.

Here’s the relevant Kotlin code using OkHttp:

val token = getToken() // returns valid string
val url = "wss://webchat-us.genesyscloud.com/ws?oauth_token=$token"

val request = Request.Builder()
 .url(url)
 .addHeader("Sec-WebSocket-Protocol", "webchat")
 .build()

val client = OkHttpClient()
client.newWebSocket(request, object : WebSocketListener() {
 override fun onOpen(webSocket: WebSocket, response: Response) {
 // never hits here
 }
 override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
 println("Failed: ${response?.code} ${t.message}")
 }
})

The logs show Failed: 401 null. I’ve checked the token expiry, it’s fresh. I’ve also tried passing the token in the Authorization: Bearer header instead of the query param, but the WebSocket spec doesn’t really support custom headers well during the handshake, and that just gives me a 400 Bad Request.

Is there a specific scope required for the OAuth token to work with the Guest API WebSocket? The docs mention urn:genesys:cloud:webchat but I’m not sure if that’s for the widget or the raw API. Also, should I be using a different endpoint for authenticated guests vs anonymous ones? We’re trying to tie the chat session to a specific user ID in our backend.

Any ideas on what’s causing the 401? I’ve been staring at this for two days. The token works for REST calls but not WS. It feels like a scope issue but I can’t find clear examples of working Kotlin code for this specific flow.