Guest API WebSocket handshake failing with 403 when bypassing default widget

Trying to spin up a custom chat UI using the raw Guest API instead of the embedded widget. We’ve got the OAuth token generation working fine via the backend, but the WebSocket connection keeps dropping immediately after the initial handshake.

Here’s the sequence:

  1. GET /api/v2/conversations/guests to grab the conversationId.
  2. GET /api/v2/conversations/conversations/{id}/endpoints to fetch the websocketUrl.
  3. Initiate WS connection to the returned URL.

The connection establishes, sends the connect frame, but then gets a 403 Forbidden on the next message send. The payload looks valid.

{
 "type": "message",
 "text": "Test message"
}

Is there a specific header or auth token required in the WebSocket frames that isn’t documented clearly? The default widget handles this invisibly, but building custom logic means I’m hitting walls. Anyone got a working snippet for the WS frame structure?

The guest token expires in 30 seconds. You need to pass access_token and refresh_token in the WS subprotocol header, not just rely on the initial handshake. Check the Sec-Websocket-Protocol header in your client.