Skip to main content

Overview

Nadoo AI provides WebSocket endpoints for bidirectional real-time communication. While SSE handles unidirectional server-to-client streaming, WebSocket connections enable interactive scenarios such as live chat with streaming responses, workspace collaboration, user notifications, and plugin debugging.

Chat

Real-time bidirectional chat with token-by-token AI response streaming, typing indicators, and message cancellation.

Notifications

Push notifications to connected users for system events, workflow completions, and workspace activity.

Workspace Collaboration

Real-time workspace events — document updates, application changes, and member activity broadcasts.

Plugin Debug

Live debug streaming during plugin execution with step-by-step traces, variable inspection, and API call logs.

Endpoints

EndpointPurposeAuth
ws://.../ws/chat/{session_id}Real-time chat streamingJWT token (query param)
ws://.../ws/notifications/{user_id}User notificationsJWT token (required)
ws://.../ws/workspaces/{workspace_id}Workspace collaborationJWT token (required)
ws://.../api/v1/ws/plugin/debug/{execution_id}Plugin debug consoleNone (execution ID scoped)

Authentication

WebSocket connections authenticate via a token query parameter containing a valid JWT access token. The server decodes the token before accepting the connection.
ws://localhost:8000/ws/chat/session-abc?token=eyJhbGciOiJIUzI1NiIs...
If the token is invalid or expired, the server closes the connection immediately with code 1008 (Policy Violation) and the reason "Invalid token". Ensure the token is refreshed before establishing the WebSocket connection.

Chat WebSocket

The chat WebSocket provides real-time bidirectional communication for interactive AI conversations.

Connection

1

Open connection

Connect with a session ID and optional JWT token:
ws://localhost:8000/ws/chat/{session_id}?token={jwt_token}
2

Receive confirmation

The server sends a connection confirmation:
{
  "type": "connected",
  "session_id": "session-abc",
  "user_id": "user-123",
  "timestamp": "2025-01-15T10:30:00Z"
}
3

Send and receive messages

Send chat messages and receive streamed AI responses in real time.

Client-to-Server Messages

Send a chat message to the AI agent:
{
  "type": "chat",
  "content": "Summarize the Q4 report",
  "application_id": "app-uuid-123",
  "chat_id": "chat-uuid-456",
  "user_id": "user-uuid-789",
  "timestamp": "2025-01-15T10:30:00Z"
}
If chat_id is omitted, a new chat session is created automatically.
Broadcast a typing indicator to other participants in the session:
{
  "type": "typing"
}
Send a ping to keep the connection alive:
{
  "type": "ping"
}
The server responds with:
{
  "type": "pong",
  "timestamp": "2025-01-15T10:30:05Z"
}

Server-to-Client Messages

Message TypeDescription
connectedConnection established successfully
response_startAI response generation has begun
text-deltaA chunk of the AI response text
sourcesKnowledge base sources used in the response
response_endAI response is complete, includes full text and token usage
typingAnother user is typing
user_disconnectedA user left the session
errorAn error occurred processing the message

Streaming Response Flow

When the server receives a chat message, it streams the AI response as a sequence of events:
// 1. Response starts
{"type": "response_start", "message_id": "msg_1234", "timestamp": "..."}

// 2. Text chunks arrive incrementally
{"type": "text-delta", "text": "Based on the Q4 report, ", "index": 0, "timestamp": "..."}
{"type": "text-delta", "text": "revenue increased by 15%.", "index": 1, "timestamp": "..."}

// 3. Knowledge base sources (if applicable)
{"type": "sources", "sources": [...], "timestamp": "..."}

// 4. Response complete
{"type": "response_end", "full_response": "Based on the Q4 report, revenue increased by 15%.", "tokens_used": 12, "response_time": 2.3, "timestamp": "..."}

Client Example

const sessionId = 'session-abc-123';
const token = 'eyJhbGciOiJIUzI1NiIs...';

const ws = new WebSocket(
  `ws://localhost:8000/ws/chat/${sessionId}?token=${token}`
);

ws.onopen = () => {
  console.log('Connected to chat');
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  switch (data.type) {
    case 'connected':
      console.log('Session ready:', data.session_id);
      break;

    case 'response_start':
      console.log('AI is responding...');
      break;

    case 'text-delta':
      // Append each text chunk to the UI
      appendToResponse(data.text);
      break;

    case 'sources':
      showSources(data.sources);
      break;

    case 'response_end':
      console.log(`Done. Tokens: ${data.tokens_used}`);
      break;

    case 'typing':
      showTypingIndicator(data.user_id);
      break;

    case 'error':
      console.error('Error:', data.message);
      break;
  }
};

// Send a message
ws.send(JSON.stringify({
  type: 'chat',
  content: 'What were the key Q4 metrics?',
  application_id: 'app-uuid-123'
}));

// Send typing indicator
ws.send(JSON.stringify({ type: 'typing' }));

// Keep alive
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }));
  }
}, 30000);

Notifications WebSocket

The notifications endpoint delivers real-time push notifications to authenticated users.

Connection

ws://localhost:8000/ws/notifications/{user_id}?token={jwt_token}
The user_id in the URL must match the sub claim in the JWT token. Mismatched values result in an immediate connection close with code 1008.

Message Format

{
  "type": "notification",
  "notification": {
    "title": "Workflow completed",
    "body": "Your data extraction workflow finished successfully.",
    "action_url": "/workflows/wf-123/results"
  },
  "timestamp": "2025-01-15T10:30:00Z"
}

Workspace Collaboration WebSocket

The workspace endpoint enables real-time collaboration features within a shared workspace.

Connection

ws://localhost:8000/ws/workspaces/{workspace_id}?token={jwt_token}

Events

When you join the workspace, all other connected users receive a user_joined event. When you disconnect, they receive user_left. Supported broadcast event types:
Event TypeDescription
user_joinedA user connected to the workspace
user_leftA user disconnected from the workspace
document_updatedA document in the knowledge base was modified
application_changedAn application’s configuration was updated
member_addedA new member was added to the workspace

Example

const ws = new WebSocket(
  `ws://localhost:8000/ws/workspaces/${workspaceId}?token=${token}`
);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (data.type === 'user_joined') {
    showToast(`${data.user_id} joined the workspace`);
  }

  if (data.type === 'document_updated') {
    refreshDocumentList();
  }
};

// Broadcast a document update to other workspace members
ws.send(JSON.stringify({
  type: 'document_updated',
  document_id: 'doc-456',
  action: 'edited'
}));

Plugin Debug WebSocket

The plugin debug endpoint streams real-time execution data during plugin runs, enabling live debugging.

Connection

ws://localhost:8000/api/v1/ws/plugin/debug/{execution_id}
This endpoint does not require a JWT token. Access is scoped by the execution_id, which must correspond to an existing plugin execution record.

Message Types

TypeDescription
execution_infoInitial execution metadata (plugin ID, tool name, status)
debug_dataPreviously recorded debug data (sent if execution has historical data)
logLog output from the plugin
traceExecution trace data
stepA discrete execution step
variableVariable state snapshot
api_callAn external API call made by the plugin
execution_completePlugin execution finished successfully
execution_errorPlugin execution failed
errorConnection or validation error

Example

const execId = 'exec-uuid-123';
const ws = new WebSocket(
  `ws://localhost:8000/api/v1/ws/plugin/debug/${execId}`
);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  switch (data.type) {
    case 'execution_info':
      console.log(`Debugging: ${data.data.tool_name}`);
      break;
    case 'log':
      appendToDebugConsole(data.data);
      break;
    case 'step':
      highlightStep(data.data);
      break;
    case 'variable':
      updateVariableInspector(data.data);
      break;
    case 'execution_complete':
      console.log('Execution finished:', data.data.result);
      break;
    case 'execution_error':
      console.error('Execution failed:', data.data.error);
      break;
  }
};

// Keep-alive ping
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send('ping');  // Server responds with 'pong'
  }
}, 30000);

Connection Management

Reconnection Strategy

WebSocket connections do not auto-reconnect like SSE. Implement reconnection logic in your client:
1

Detect disconnect

Listen for the onclose event on the WebSocket.
2

Apply backoff

Wait with exponential backoff: 1s, 2s, 4s, 8s, up to a maximum of 30s.
3

Reconnect

Open a new WebSocket connection with a fresh JWT token if the previous one expired.
4

Restore state

After reconnecting, re-fetch any missed messages from the REST API (e.g., GET /api/v1/chat/{app_id}/messages).
function createReconnectingWebSocket(url, maxRetries = 10) {
  let retries = 0;
  let ws;

  function connect() {
    ws = new WebSocket(url);

    ws.onopen = () => {
      retries = 0; // Reset on successful connection
    };

    ws.onclose = (event) => {
      if (retries < maxRetries) {
        const delay = Math.min(1000 * Math.pow(2, retries), 30000);
        console.log(`Reconnecting in ${delay}ms...`);
        setTimeout(connect, delay);
        retries++;
      }
    };

    return ws;
  }

  return connect();
}

Connection Statistics

Monitor active WebSocket connections via the stats endpoint:
GET /ws/stats
{
  "total_connections": 42,
  "active_sessions": 15,
  "active_users": 12,
  "details": {
    "sessions": {
      "session-abc": 2,
      "workspace_ws-123": 5
    },
    "users": {
      "user-1": 3,
      "user-2": 1
    }
  }
}

WebSocket vs SSE

For most chatbot and streaming use cases, SSE is the recommended protocol. It is simpler to implement, reconnects automatically, and works reliably through HTTP proxies and load balancers. Use WebSocket only when you need bidirectional communication.
FeatureSSEWebSocket
DirectionServer to clientBidirectional
ProtocolHTTP (text/event-stream)ws:// or wss://
ReconnectionBuilt-in auto-reconnectManual implementation required
Mid-stream inputNot supportedSupported (cancel, interrupt, new messages)
Typing indicatorsNot supportedSupported
Browser supportNative EventSource APINative WebSocket API
Proxy compatibilityExcellent (standard HTTP)May require proxy configuration
Best forChat streaming, workflow progressInteractive chat, collaboration, debugging

Next Steps