Client lifecycle

Use these guidelines when maintaining a WebSocket client and its conversation state. For the message format, see Handle WebSocket messages.

Manage conversation state

A WebSocket connection represents a session. The first question without a threadId creates a conversation thread for that WebSocket. Context-aware follow-up conversations depend on keeping the same WebSocket session open.

If the socket closes, reconnecting creates a new session and does not restore the previous WebSocket session history. Provide a New conversation action that clears the active thread ID and starts a fresh conversation.

Keep a connection alive

For long-lived connections, send a heartbeat when there is no chat activity:

{
  "action": "heartbeat"
}

The service can reply with an info message containing pong: true. The server also responds to WebSocket ping frames with pong. Heartbeats can help prevent network infrastructure from closing an idle connection, but they cannot restore a session after the connection has been lost.

Collect feedback (optional)

Send feedback for an answer using its thread and message identifiers:

{
  "action": "feedback",
  "threadId": "[THREAD_ID]",
  "messageId": "[MESSAGE_ID]",
  "rating": "positive",
  "feedbackComment": "Very helpful answer"
}

Use positive or negative for rating.

feedbackComment is optional.

Treat visitor comments as potentially sensitive information and apply your organization’s privacy and retention requirements.

Client lifecycle

A client should:

  1. Open the WebSocket and enable the question input after the connection is established.
  2. Prevent a second question while a turn is in progress, unless the implementation deliberately supports cancellation.
  3. Clear the per-turn buffer before sending a question.
  4. Append each delta.text value and render it immediately.
  5. Apply the final answer rule when response arrives, then render sources and metadata.
  6. Clear loading state and re-enable input after complete or after safely handling the response.
  7. Retain the active threadId for follow-up questions.
  8. Clear the active thread and explain the state change when starting a new conversation or recovering from a lost connection.
  9. Ignore events from an old socket after reconnecting.

Minimal browser example

The following example shows the core message handling. Replace the endpoint with the value supplied for your implementation. This example is not a complete production client; add input validation, safe message parsing, reconnect handling, stale-socket protection, and user-interface code before using it in a live site.


const ws = new WebSocket(
  'wss://dxp.squiz.cloud/__dxp/[REGION]/search-conversations/[ORGANIZATION_ID]/ws/v2/[CHATBOT_ID]'
);

let threadId;
let answerBuffer = '';
let waitingForAnswer = false;

ws.addEventListener('open', () => {
  // Enable the question input.
});

ws.addEventListener('message', event => {
  const message = JSON.parse(event.data);

  if (message.threadId) {
    threadId = message.threadId;
  }

  switch (message.type) {
    case 'info':
      // Save messageId when feedback is supported.
      break;

    case 'delta':
      answerBuffer += message.text ?? '';
      // Update the assistant message with answerBuffer.
      break;

    case 'response': {
      const answer = answerBuffer.length > 0
        ? answerBuffer
        : (message.answer ?? '');
      // Render answer, message.sources, and other metadata.
      waitingForAnswer = false;
      break;
    }

    case 'complete':
      answerBuffer = '';
      waitingForAnswer = false;
      // Allow the next question.
      break;

    case 'error':
      if (message.step === 'cancelled') {
        break;
      }
      answerBuffer = '';
      waitingForAnswer = false;
      // Show a safe retry message.
      break;

    default:
      // Ignore message types that this client does not use.
      break;
  }
});

function sendQuestion(text) {
  if (ws.readyState !== WebSocket.OPEN || waitingForAnswer) {
    return;
  }

  answerBuffer = '';
  waitingForAnswer = true;

  ws.send(JSON.stringify({
    action: 'chat',
    text,
    ...(threadId ? { threadId } : {})
  }));
}