Skip to content

Admin WebSocket Connection

Establishes a real-time WebSocket connection for admin chat functionality.

WebSocket URL

ws://localhost:8000/v1/chat/ws/admin/?token=<auth_token>
wss://api.luxmart.site/v1/chat/ws/admin/?token=<auth_token>

Authentication

Pass the admin's authentication token as a query parameter:

?token=<admin_auth_token>

Note: Only users with admin role can connect.

Connection Lifecycle

1. Connect

const token = "your_admin_token_here";
const ws = new WebSocket(`ws://localhost:8000/v1/chat/ws/admin/?token=${encodeURIComponent(token)}`);

2. Connection Events

On Open:

ws.onopen = () => {
  console.log("Admin chat connected");
};

On Message:

ws.onmessage = (event) => {
  const payload = JSON.parse(event.data);
  // Handle different event types
};

On Close:

ws.onclose = () => {
  console.log("Admin chat disconnected");
  // Implement reconnection logic
};

On Error:

ws.onerror = (error) => {
  console.error("WebSocket error:", error);
};

Event Types

1. Receiving Messages

Event Type: message

Payload:

{
  "type": "message",
  "payload": {
    "message": {
      "id": 124,
      "session_id": 1,
      "sender_type": "store",
      "content": "I have a question about shipping",
      "is_read": false,
      "created_at": "2025-12-16T10:25:00Z"
    }
  }
}

Important: Admins receive messages from ALL their claimed sessions, not just the active one.

Example Handler:

if (payload.type === "message") {
  const message = payload.payload.message;

  if (message.session_id === activeSessionId) {
    // Display in current chat view
    displayMessage(message);
  } else {
    // Update session list to show unread message
    updateSessionList(message.session_id);
  }
}

2. New Message Notification

Event Type: new_notification

Sent when a store sends a message while admin is on a different page (not on messages page).

Payload:

{
  "type": "new_notification",
  "payload": {
    "count": 5
  }
}

Example Handler:

if (payload.type === "new_notification") {
  updateNotificationBadge(payload.payload.count);
}

3. Session Updates

Event Type: session_update

Sent when a new session is created or session status changes.

Payload:

{
  "type": "session_update",
  "payload": {
    "session": {
      "id": 5,
      "store_id": 10,
      "status": "open",
      "created_at": "2025-12-16T11:00:00Z"
    }
  }
}

4. Typing Indicator

Event Type: typing

Indicates when a store is typing.

Payload:

{
  "type": "typing",
  "payload": {
    "session_id": 1,
    "is_typing": true,
    "sender_type": "store"
  }
}

Example Handler:

if (payload.type === "typing") {
  if (payload.payload.session_id === activeSessionId) {
    showTypingIndicator(payload.payload.is_typing);
  }
}

5. Error Messages

Event Type: error

Payload:

{
  "type": "error",
  "payload": {
    "message": "Failed to send message",
    "code": "SEND_FAILED"
  }
}

Sending Messages

1. Send Text Message

Send via WebSocket:

ws.send(JSON.stringify({
  type: "message",
  payload: {
    session_id: 1,
    content: "I can help you with that!"
  }
}));

Required Fields: - session_id: The ID of the chat session - content: The message text

Response: You'll receive the message back with full details including id and created_at.

2. Send Typing Indicator

// Admin started typing
ws.send(JSON.stringify({
  type: "typing",
  payload: {
    session_id: 1,
    is_typing: true
  }
}));

// Admin stopped typing
setTimeout(() => {
  ws.send(JSON.stringify({
    type: "typing",
    payload: {
      session_id: 1,
      is_typing: false
    }
  }));
}, 3000);

Complete Example

class AdminChat {
  constructor(token) {
    this.token = token;
    this.ws = null;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;
    this.activeSessions = new Map();
  }

  connect() {
    const wsUrl = `ws://localhost:8000/v1/chat/ws/admin/?token=${encodeURIComponent(this.token)}`;
    this.ws = new WebSocket(wsUrl);

    this.ws.onopen = () => {
      console.log("Admin chat connected");
      this.reconnectAttempts = 0;
    };

    this.ws.onmessage = (event) => {
      const payload = JSON.parse(event.data);
      this.handleEvent(payload);
    };

    this.ws.onclose = () => {
      console.log("Admin chat disconnected");
      this.reconnect();
    };

    this.ws.onerror = (error) => {
      console.error("WebSocket error:", error);
    };
  }

  handleEvent(payload) {
    switch (payload.type) {
      case "message":
        if (payload.payload?.message) {
          this.handleMessage(payload.payload.message);
        }
        break;

      case "new_notification":
        if (payload.payload?.count !== undefined) {
          this.updateNotificationCount(payload.payload.count);
        }
        break;

      case "session_update":
        if (payload.payload?.session) {
          this.handleSessionUpdate(payload.payload.session);
        }
        break;

      case "typing":
        if (payload.payload?.session_id) {
          this.handleTyping(
            payload.payload.session_id,
            payload.payload.is_typing
          );
        }
        break;

      case "error":
        console.error("Chat error:", payload.payload?.message);
        break;
    }
  }

  handleMessage(message) {
    // Store message
    if (!this.activeSessions.has(message.session_id)) {
      this.activeSessions.set(message.session_id, []);
    }
    this.activeSessions.get(message.session_id).push(message);

    // Display or update UI
    if (message.sender_type === "store") {
      this.displayStoreMessage(message);
    }
  }

  sendMessage(sessionId, content) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({
        type: "message",
        payload: {
          session_id: sessionId,
          content: content
        }
      }));
    }
  }

  sendTyping(sessionId, isTyping) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({
        type: "typing",
        payload: {
          session_id: sessionId,
          is_typing: isTyping
        }
      }));
    }
  }

  reconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
      console.log(`Reconnecting in ${delay}ms...`);
      setTimeout(() => this.connect(), delay);
    }
  }

  disconnect() {
    if (this.ws) {
      this.ws.close();
    }
  }

  displayStoreMessage(message) {
    // Implement your UI logic here
    console.log(`Store (Session ${message.session_id}): ${message.content}`);
  }

  updateNotificationCount(count) {
    // Update notification badge
    console.log(`Unread notifications: ${count}`);
  }

  handleSessionUpdate(session) {
    // Update session list
    console.log(`Session updated: ${session.id} - ${session.status}`);
  }

  handleTyping(sessionId, isTyping) {
    // Show/hide typing indicator for specific session
    console.log(`Session ${sessionId}: typing=${isTyping}`);
  }
}

// Usage
const adminChat = new AdminChat(adminToken);
adminChat.connect();

// Send message to a specific session
adminChat.sendMessage(1, "Hello! How can I help you today?");

// Send typing indicator
adminChat.sendTyping(1, true);

Managing Multiple Sessions

Admins can handle multiple chat sessions simultaneously:

1. Track Active Sessions

// Store messages per session
const sessionMessages = new Map();

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

  if (payload.type === "message") {
    const msg = payload.payload.message;

    if (!sessionMessages.has(msg.session_id)) {
      sessionMessages.set(msg.session_id, []);
    }

    sessionMessages.get(msg.session_id).push(msg);

    // Update UI based on active session
    if (msg.session_id === currentActiveSessionId) {
      displayMessage(msg);
    } else {
      showUnreadIndicator(msg.session_id);
    }
  }
};

2. Switch Between Sessions

function switchToSession(sessionId) {
  currentActiveSessionId = sessionId;

  // Load message history for this session
  const messages = sessionMessages.get(sessionId) || [];
  displayMessages(messages);

  // Mark messages as read
  markSessionMessagesAsRead(sessionId);
}

Notification System

When the admin is NOT on the messages page, they receive notifications for new messages:

1. Listen for Notifications

if (payload.type === "new_notification") {
  const unreadCount = payload.payload.count;

  // Update badge on bell icon
  updateBadge(unreadCount);

  // Optionally show browser notification
  if (Notification.permission === "granted") {
    new Notification("New Message", {
      body: "You have a new message from a store",
      icon: "/notification-icon.png"
    });
  }
}

2. Fetch Notification Details

Use the REST API to get full notification details:

// GET /v1/chat/admin/notifications/
fetch("/v1/chat/admin/notifications/", {
  headers: {
    "Authorization": `Token ${adminToken}`
  }
})
  .then(res => res.json())
  .then(data => {
    displayNotifications(data.data);
  });

Best Practices

  1. Always URL-encode the token when passing as query parameter
  2. Implement auto-reconnection with exponential backoff
  3. Handle multiple sessions by tracking messages per session
  4. Update session lists when receiving messages from non-active sessions
  5. Show typing indicators only for the active session
  6. Mark messages as read when viewing a session
  7. Handle notifications when admin is on other pages
  8. Buffer messages during disconnection
  9. Implement UI feedback for connection status
  10. Auto-scroll to latest message in active session