Genesys Cloud WebSocket subscription timing out with 401 immediately after auth

I’m trying to set up a persistent WebSocket connection to listen for conversation events in my .NET integration. We’re moving away from polling the Notification API because it’s too heavy on the API limits. The goal is to subscribe to conversation:all events.

I followed the documentation for the Notification API WebSocket. The docs say:

“To establish a WebSocket connection, you must first authenticate by sending a JSON object with your OAuth access token.”

Here is the C# code I’m using with the System.Net.WebSockets.ClientWebSocket:

using var webSocket = new ClientWebSocket();
var uri = new Uri("wss://api.us.genesyscloud.com/v2/analytics/conversations/details/query/ws"); // Wait, is this the right endpoint?

var options = new ClientWebSocketOptions();
webSocket.Options.SetRequestHeader("Authorization", $"Bearer {token}");

await webSocket.ConnectAsync(uri, CancellationToken.None);

// Send subscription message
var subMessage = new { channel = "conversation", query = new { } };
var json = JsonSerializer.Serialize(subMessage);
var bytes = Encoding.UTF8.GetBytes(json);
await webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);

The connection opens, but immediately after I send the subscription JSON, I get a WebSocketError with a 401 Unauthorized close code. The token I’m using is valid for other calls like GET /api/v2/users/me. I checked the token scopes and it has conversation:view and notification:subscribe.

I also tried sending the token as a query parameter ?access_token={token} on the connection URL, but that gives me a 400 Bad Request. The docs are vague on whether the token goes in the header or the query string for the WebSocket handshake.

Also, I noticed the endpoint in the docs example is wss://api.genesyscloud.com/v2/notifications/ws. Is the analytics endpoint wrong for real-time event streaming? I’m confused about which WebSocket URL to hit for conversation lifecycle events versus analytics data.

Any ideas why the auth is failing right after the handshake?

Docs say send the token in the first message, but .NET’s client handshake often times out before you get to send it. You need to pass the token in the query string instead.

var ws = new ClientWebSocket();
var uri = new Uri("wss://api.mypurecloud.com/api/v2/analytics/conversations/events?access_token=YOUR_TOKEN");
await ws.ConnectAsync(uri, CancellationToken.None);

Works instantly. No 401.