WebSocket API

ProductFlo implements a robust WebSocket system that supports both native WebSocket and Socket.IO protocols for real-time bidirectional communication. This unified interface powers various interactive features including chat, AI interactions, collaborative editing, and system notifications.

Connection Endpoints

Main WebSocket

/ws - Primary WebSocket endpoint (no authentication required)

Secure WebSocket

/ws/secure - Authenticated WebSocket endpoint requiring token

Socket.IO

/ws - Socket.IO compatible endpoint (conditionally mounted at runtime)

Message Structure

All WebSocket messages follow a consistent structure:

{
  "code": "chat",           // Required: Message type (from CodeType enum)
  "user_id": "user-123",    // Required: Sender user ID
  "room_id": "room-456",    // Optional: Target room for broadcasting
  "timestamp": "2024-05-19T10:30:45Z", // Optional: ISO timestamp (auto-added if omitted)
  "data": {                 // Required: Message content
    // Type-specific payload
  }
}

Connection Examples

Standard WebSocket (Non-Authenticated)

// Connect to standard WebSocket endpoint
const socket = new WebSocket('wss://api.productflo.io/ws');

// Handle connection open
socket.onopen = () => {
  console.log('Connection established');
};

// Listen for messages
socket.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

// Send a message
function sendMessage(message) {
  socket.send(JSON.stringify({
    code: "chat",
    user_id: "user-123", // Use the user_id assigned by server or provided
    room_id: "product-456",
    data: {
      message: message,
      sender: "user-123"
    }
  }));
}

Secure WebSocket (Authenticated)

// Connect to secure WebSocket endpoint with authentication
const socket = new WebSocket('wss://api.productflo.io/ws/secure?access_token=YOUR_JWT_TOKEN');

// Handle connection open
socket.onopen = () => {
  console.log('Authenticated connection established');
};

// Listen for messages
socket.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

// Send a message
function sendMessage(message) {
  socket.send(JSON.stringify({
    code: "chat",
    user_id: "user-123", // This should match the authenticated user ID
    room_id: "product-456",
    data: {
      message: message,
      sender: "user-123"
    }
  }));
}

Socket.IO Client

import { io } from 'socket.io-client';

// Connect to Socket.IO endpoint
const socket = io('https://api.productflo.io', {
  path: '/ws',
  extraHeaders: { 'User-ID': 'user-123' }, // Optional user ID
  auth: { token: 'YOUR_JWT_TOKEN' }, // Optional token for authentication
  reconnection: true
});

// Handle connection
socket.on('connect', () => {
  console.log('Connected to Socket.IO');
});

// Listen for message events
socket.on('message', (data) => {
  console.log('Message received:', data);
  
  // Handle different message types
  if (data.data && data.data.code) {
    switch(data.data.code) {
      case 'chat_response':
        handleChatResponse(data.data);
        break;
      case 'system':
        handleSystemMessage(data.data);
        break;
      // Handle other message types
    }
  }
});

// Send a message
function sendMessage(message) {
  socket.emit('message', {
    code: "chat",
    user_id: "user-123",
    room_id: "product-456",
    data: {
      message: message,
      sender: "user-123"
    }
  });
}

Authentication

WebSocket connections support multiple authentication methods:

Secure Endpoint

For authenticated connections, use the /ws/secure endpoint which requires a token:

// Using query parameter
const socket = new WebSocket('wss://api.productflo.io/ws/secure?access_token=YOUR_JWT_TOKEN');

// OR using cookies (if token was set in cookies during login)
const socket = new WebSocket('wss://api.productflo.io/ws/secure');

Non-Authenticated Connection

For connections that don’t require authentication, use the standard /ws endpoint:

const socket = new WebSocket('wss://api.productflo.io/ws');

The system will assign a unique user ID if none is provided.

Socket.IO Authentication

When using Socket.IO, provide authentication information in headers or cookies:

const socket = io('https://api.productflo.io', {
  path: '/ws',
  extraHeaders: { 'User-ID': 'user-123' }, // Optional user ID
  auth: { token: 'YOUR_JWT_TOKEN' } // Optional token
});

Message Types (CodeType)

The WebSocket system uses a set of predefined message types through the CodeType enum:

Sending Types (Client to Server)

chat
string

Regular chat messages

haitch_chat
string

AI assistant chat interaction

idea
string

Product idea generation

documentation
string

Document generation request

engineering
string

Engineering-specific requests

Receiving Types (Server to Client)

chat_response
string

Response to chat messages

idea_response
string

Product idea generation response

documentation_response
string

Document generation response

engineering_response
string

Engineering-specific responses

system
string

System notifications and status updates

notification
string

User notifications

logs
string

Debug and process logs

Status Types

All responses include a status field:

success
string

Operation completed successfully

error
string

Error occurred during operation

pending
string

Operation is still in progress

Basic Usage Examples

Chat Message

// Send a chat message
socket.send(JSON.stringify({
  code: "chat",
  user_id: "user-123",
  room_id: "product-456",
  data: {
    message: "Has anyone reviewed the latest design update?",
    sender: "user-123"
  }
}));

AI Assistant Interaction

// Send a message to the AI assistant
socket.send(JSON.stringify({
  code: "haitch_chat",
  user_id: "user-123",
  room_id: "product-456",
  data: {
    message: "What materials would be best for a waterproof enclosure?",
    sender: "user-123"
  }
}));

Document Generation Request

// Request document generation
socket.send(JSON.stringify({
  code: "documentation",
  user_id: "user-123",
  room_id: "product-456",
  data: {
    message: "Generate a technical specification document for our thermostat",
    sender: "user-123",
    attached_files: ["requirements.pdf"]
  }
}));

Event Handling Example

// Set up event handling for different message types
socket.onmessage = (event) => {
  const data = JSON.parse(event.data);
  
  switch(data.code) {
    case "chat_response":
      displayChatMessage(data);
      break;
    
    case "idea_response":
      showIdeaResults(data);
      break;
    
    case "documentation_response":
      handleDocumentGeneration(data);
      break;
    
    case "engineering_response":
      processEngineeringResponse(data);
      break;
    
    case "system":
      handleSystemMessage(data);
      break;
    
    case "notification":
      showNotification(data);
      break;
      
    case "error":
      handleError(data);
      break;
      
    default:
      console.log("Unhandled message type:", data);
  }
};

Error Handling

Error responses follow a standardized format:

{
  "status": "error",
  "code": "system",
  "user_id": "user-123",
  "timestamp": "2024-05-19T10:30:45Z",
  "data": {
    "message": "Error message here",
    "details": {
      "error_code": "SPECIFIC_ERROR_CODE",
      "message": "Detailed error message"
    }
  }
}

Common error codes:

  • MESSAGE_CODE_REQUIRED: Missing message code
  • UNSUPPORTED_MESSAGE_TYPE: Unknown message type
  • ROOM_ID_REQUIRED: Room ID not provided
  • FAILED_TO_PROCESS_MESSAGE: General processing error

Connection Lifecycle

The WebSocket connection follows a distinct lifecycle:

  1. Connection: Client establishes connection with optional authentication
  2. Welcome: Server sends a system message confirming connection
  3. Cookie: Server sets a user ID cookie if not present
  4. Interaction: Exchange of messages based on application needs
  5. Disconnection: Triggered by client disconnect or timeout

When a client connects, they’ll receive a welcome message:

{
  "status": "success",
  "code": "system",
  "user_id": "user-123",
  "timestamp": "2024-05-19T10:30:45Z",
  "data": {
    "message": "Connected to ProductFlo WebSocket server",
    "details": {
      "user_id": "user-123",
      "timestamp": "2024-05-19T10:30:45Z"
    }
  }
}

Implementation Details

The WebSocket system is built on three key components:

  1. WebSocketManager (utils/websocket_manager.py): Central component for connection management, message routing, and broadcasting

  2. Message Types (models/websocket.py): Pydantic models for typed message validation

  3. Route Handlers (api/routes/websocket.py): FastAPI WebSocket endpoint definitions

The implementation supports both direct point-to-point messages and room-based broadcasting patterns.

Best Practices

When implementing client applications:

  1. Reconnection Logic: Implement automatic reconnection with exponential backoff
  2. Message Validation: Validate outgoing messages against the API schema
  3. Error Handling: Process error responses and retry when appropriate
  4. Event Delegation: Use an event-based architecture to handle different message types
  5. Typing Indicators: Send typing indicators for improved user experience

Next Steps