> ## Documentation Index
> Fetch the complete documentation index at: https://twigz.arqqin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat System

> Real-time chat system for customer support and store communication

## Overview

The Chat System API provides comprehensive messaging functionality for customer support, including direct messaging between customers and stores, admin support, typing indicators, and message management.

**Location:** `convex/shared/chat.ts`

<CardGroup cols={2}>
  <Card title="Real-time Messaging" icon="message-circle">
    Instant messaging with typing indicators and read receipts
  </Card>

  <Card title="Image Support" icon="image">
    Send images with automatic validation and compression
  </Card>

  <Card title="Conversation Management" icon="users">
    Create, archive, and manage conversations between users
  </Card>

  <Card title="Admin Support" icon="headphones">
    Built-in admin support system with conversation transfer
  </Card>
</CardGroup>

## Create or Get Conversation

Create a new conversation or get existing one for customer support.

<ParamField path="orderId" type="Id<'orders'>">
  Optional order ID if conversation is about a specific order
</ParamField>

<ParamField path="storeId" type="Id<'stores'>">
  Optional store ID for direct store messaging
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.mutation(api.shared.chat.createOrGetConversation, {
    orderId: "order123456789",
    storeId: "store123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  const result = await convex.mutation("shared.chat.createOrGetConversation", {
    orderId: "order123456789",
    storeId: "store123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "conversationId": "conv123456789",
    "conversationType": "customer_to_store"
  }
  ```
</ResponseExample>

<Note>
  The system automatically determines conversation type based on:

  * If `storeId` and `orderId` are provided and customer has orders with store → `customer_to_store`
  * Otherwise → `customer_to_admin`
</Note>

## Send Message

Send a text or image message in a conversation.

<ParamField path="conversationId" type="Id<'conversations'>" required>
  Conversation ID to send message to
</ParamField>

<ParamField path="content" type="string" required>
  Message content
</ParamField>

<ParamField path="messageType" type="'text' | 'image'" required>
  Type of message being sent
</ParamField>

<ParamField path="imageAttachment" type="object">
  Image attachment details (required for image messages)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Send text message
  const messageId = await convex.mutation(api.shared.chat.sendMessage, {
    conversationId: "conv123456789",
    content: "Hello, I need help with my order",
    messageType: "text"
  });

  // Send image message
  const imageMessageId = await convex.mutation(api.shared.chat.sendMessage, {
    conversationId: "conv123456789",
    content: "Here's a photo of the issue",
    messageType: "image",
    imageAttachment: {
      fileId: "file123456789",
      fileName: "issue.jpg",
      fileType: "image/jpeg",
      fileSize: 1024000,
      width: 1920,
      height: 1080
    }
  });
  ```

  ```javascript JavaScript theme={null}
  // Send text message
  const messageId = await convex.mutation("shared.chat.sendMessage", {
    conversationId: "conv123456789",
    content: "Hello, I need help with my order",
    messageType: "text"
  });

  // Send image message
  const imageMessageId = await convex.mutation("shared.chat.sendMessage", {
    conversationId: "conv123456789",
    content: "Here's a photo of the issue",
    messageType: "image",
    imageAttachment: {
      fileId: "file123456789",
      fileName: "issue.jpg",
      fileType: "image/jpeg",
      fileSize: 1024000,
      width: 1920,
      height: 1080
    }
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  "msg123456789"
  ```
</ResponseExample>

## Mark Messages as Read

Mark messages as read in a conversation.

<ParamField path="conversationId" type="Id<'conversations'>" required>
  Conversation ID
</ParamField>

<ParamField path="messageIds" type="Array<Id<'messages'>>">
  Specific message IDs to mark as read (optional - marks all unread if not provided)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Mark all unread messages as read
  const result = await convex.mutation(api.shared.chat.markMessagesAsRead, {
    conversationId: "conv123456789"
  });

  // Mark specific messages as read
  const result = await convex.mutation(api.shared.chat.markMessagesAsRead, {
    conversationId: "conv123456789",
    messageIds: ["msg123456789", "msg987654321"]
  });
  ```

  ```javascript JavaScript theme={null}
  // Mark all unread messages as read
  const result = await convex.mutation("shared.chat.markMessagesAsRead", {
    conversationId: "conv123456789"
  });

  // Mark specific messages as read
  const result = await convex.mutation("shared.chat.markMessagesAsRead", {
    conversationId: "conv123456789",
    messageIds: ["msg123456789", "msg987654321"]
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "markedCount": 3
  }
  ```
</ResponseExample>

## Archive Conversation

Archive a conversation to remove it from active conversations.

<ParamField path="conversationId" type="Id<'conversations'>" required>
  Conversation ID to archive
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.shared.chat.archiveConversation, {
    conversationId: "conv123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  await convex.mutation("shared.chat.archiveConversation", {
    conversationId: "conv123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  null
  ```
</ResponseExample>

## Delete Message

Delete a message (soft delete - marks as deleted but preserves for audit).

<ParamField path="messageId" type="Id<'messages'>" required>
  Message ID to delete
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.shared.chat.deleteMessage, {
    messageId: "msg123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  await convex.mutation("shared.chat.deleteMessage", {
    messageId: "msg123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  null
  ```
</ResponseExample>

<Note>
  Only the message sender can delete their own messages. Admins can delete any message.
</Note>

## Update Typing Status

Update user's typing status in a conversation for real-time indicators.

<ParamField path="conversationId" type="Id<'conversations'>" required>
  Conversation ID
</ParamField>

<ParamField path="isTyping" type="boolean" required>
  Whether user is currently typing
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Start typing
  await convex.mutation(api.shared.chat.updateTypingStatus, {
    conversationId: "conv123456789",
    isTyping: true
  });

  // Stop typing
  await convex.mutation(api.shared.chat.updateTypingStatus, {
    conversationId: "conv123456789",
    isTyping: false
  });
  ```

  ```javascript JavaScript theme={null}
  // Start typing
  await convex.mutation("shared.chat.updateTypingStatus", {
    conversationId: "conv123456789",
    isTyping: true
  });

  // Stop typing
  await convex.mutation("shared.chat.updateTypingStatus", {
    conversationId: "conv123456789",
    isTyping: false
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  null
  ```
</ResponseExample>

## Get Conversations

Retrieve user's conversations with pagination.

<ParamField path="status" type="'active' | 'archived' | 'closed'">
  Filter by conversation status (default: active conversations)
</ParamField>

<ParamField path="limit" type="number">
  Maximum number of conversations to return (default: 50)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const conversations = await convex.query(api.shared.chat.getConversations, {
    status: "active",
    limit: 25
  });
  ```

  ```javascript JavaScript theme={null}
  const conversations = await convex.query("shared.chat.getConversations", {
    status: "active",
    limit: 25
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_id": "conv123456789",
      "_creationTime": 1640995200000,
      "conversationType": "customer_to_store",
      "storeId": "store123456789",
      "storeName": "Pizza Palace",
      "orderId": "order123456789",
      "lastMessageAt": 1640995800000,
      "lastMessagePreview": "Thank you for your help!",
      "unreadCountCustomer": 0,
      "unreadCountRecipient": 0,
      "status": "active"
    }
  ]
  ```
</ResponseExample>

## Get Conversation Details

Get detailed information about a specific conversation.

<ParamField path="conversationId" type="Id<'conversations'>" required>
  Conversation ID to get details for
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const conversation = await convex.query(api.shared.chat.getConversation, {
    conversationId: "conv123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  const conversation = await convex.query("shared.chat.getConversation", {
    conversationId: "conv123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "_id": "conv123456789",
    "_creationTime": 1640995200000,
    "customerId": "cust123456789",
    "customerName": "John Doe",
    "conversationType": "customer_to_store",
    "storeId": "store123456789",
    "storeName": "Pizza Palace",
    "orderId": "order123456789",
    "orderSummary": {
      "_id": "order123456789",
      "totalAmount": 25.50,
      "status": "delivered",
      "orderDate": 1640995000000
    },
    "status": "active",
    "lastMessageAt": 1640995800000,
    "unreadCountCustomer": 0,
    "unreadCountRecipient": 0
  }
  ```
</ResponseExample>

## Get Messages

Retrieve messages from a conversation with pagination.

<ParamField path="conversationId" type="Id<'conversations'>" required>
  Conversation ID to get messages from
</ParamField>

<ParamField path="limit" type="number">
  Maximum number of messages to return (default: 50)
</ParamField>

<ParamField path="cursor" type="string">
  Pagination cursor for loading more messages
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.query(api.shared.chat.getMessages, {
    conversationId: "conv123456789",
    limit: 25
  });
  ```

  ```javascript JavaScript theme={null}
  const result = await convex.query("shared.chat.getMessages", {
    conversationId: "conv123456789",
    limit: 25
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "messages": [
      {
        "_id": "msg123456789",
        "_creationTime": 1640995800000,
        "senderType": "customer",
        "senderName": "John Doe",
        "messageType": "text",
        "content": "Thank you for your help!",
        "isRead": true,
        "readAt": 1640995900000,
        "isDeleted": false
      },
      {
        "_id": "msg987654321",
        "_creationTime": 1640995700000,
        "senderType": "store",
        "senderName": "Pizza Palace Support",
        "messageType": "text",
        "content": "You're welcome! Is there anything else I can help you with?",
        "isRead": true,
        "readAt": 1640995750000,
        "isDeleted": false
      }
    ],
    "isDone": true,
    "continueCursor": undefined
  }
  ```
</ResponseExample>

## Get Unread Count

Get total unread message count for the authenticated user.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.query(api.shared.chat.getUnreadCount, {});
  ```

  ```javascript JavaScript theme={null}
  const result = await convex.query("shared.chat.getUnreadCount", {});
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "totalUnread": 5
  }
  ```
</ResponseExample>

## Get Typing Indicators

Get current typing indicators for a conversation.

<ParamField path="conversationId" type="Id<'conversations'>" required>
  Conversation ID to get typing indicators for
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const indicators = await convex.query(api.shared.chat.getTypingIndicators, {
    conversationId: "conv123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  const indicators = await convex.query("shared.chat.getTypingIndicators", {
    conversationId: "conv123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "userId": "user123456789",
      "userType": "store",
      "userName": "Pizza Palace Support",
      "lastTypingAt": 1640996000000
    }
  ]
  ```
</ResponseExample>

## Can Chat with Store

Check if a customer can start a direct conversation with a store.

<ParamField path="storeId" type="Id<'stores'>" required>
  Store ID to check chat eligibility for
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.query(api.shared.chat.canChatWithStore, {
    storeId: "store123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  const result = await convex.query("shared.chat.canChatWithStore", {
    storeId: "store123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "canChat": true,
    "reason": undefined,
    "orderIds": ["order123456789", "order987654321"]
  }
  ```
</ResponseExample>

<Note>
  Customers can only chat with stores if:

  1. Direct messaging is enabled globally
  2. Store has direct messaging enabled
  3. Customer has at least one non-cancelled order with the store
</Note>

## Get Chat Settings

Get current chat system settings.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const settings = await convex.query(api.shared.chat.getChatSettings, {});
  ```

  ```javascript JavaScript theme={null}
  const settings = await convex.query("shared.chat.getChatSettings", {});
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "directMessagingEnabled": true,
    "maxImageSizeMB": 5,
    "allowedImageTypes": ["image/jpeg", "image/png", "image/gif", "image/webp"],
    "adminSupportEnabled": true
  }
  ```
</ResponseExample>

## Real-time Integration

The chat system supports real-time updates through Convex subscriptions:

<CodeGroup>
  ```typescript React Hook theme={null}
  import { useQuery, useMutation } from 'convex/react';
  import { api } from './convex/_generated/api';

  function useChat(conversationId: string) {
    // Subscribe to messages
    const messages = useQuery(api.shared.chat.getMessages, { 
      conversationId,
      limit: 50 
    });
    
    // Subscribe to typing indicators
    const typingIndicators = useQuery(api.shared.chat.getTypingIndicators, {
      conversationId
    });
    
    // Subscribe to unread count
    const unreadCount = useQuery(api.shared.chat.getUnreadCount, {});
    
    const sendMessage = useMutation(api.shared.chat.sendMessage);
    const markAsRead = useMutation(api.shared.chat.markMessagesAsRead);
    const updateTyping = useMutation(api.shared.chat.updateTypingStatus);
    
    return {
      messages,
      typingIndicators,
      unreadCount,
      sendMessage,
      markAsRead,
      updateTyping
    };
  }
  ```

  ```typescript Vue Composition API theme={null}
  import { useConvexQuery, useConvexMutation } from '@convex-vue/core';

  export function useChat(conversationId: string) {
    const messages = useConvexQuery('shared.chat.getMessages', { 
      conversationId,
      limit: 50 
    });
    
    const typingIndicators = useConvexQuery('shared.chat.getTypingIndicators', {
      conversationId
    });
    
    const sendMessage = useConvexMutation('shared.chat.sendMessage');
    const markAsRead = useConvexMutation('shared.chat.markMessagesAsRead');
    const updateTyping = useConvexMutation('shared.chat.updateTypingStatus');
    
    return {
      messages,
      typingIndicators,
      sendMessage,
      markAsRead,
      updateTyping
    };
  }
  ```
</CodeGroup>

## Message Types

The system supports various message types:

<CardGroup cols={2}>
  <Card title="Text Messages" icon="type">
    Standard text messages with full UTF-8 support
  </Card>

  <Card title="Image Messages" icon="image">
    Images with automatic validation and compression
  </Card>

  <Card title="System Messages" icon="settings">
    Automated system notifications and updates
  </Card>

  <Card title="Transfer Notices" icon="arrow-right">
    Messages indicating conversation transfers
  </Card>
</CardGroup>

## Error Handling

<AccordionGroup>
  <Accordion title="Conversation not found">
    **Status Code**: `404`

    ```json theme={null}
    {
      "error": "Conversation not found"
    }
    ```
  </Accordion>

  <Accordion title="Unauthorized access">
    **Status Code**: `403`

    ```json theme={null}
    {
      "error": "Unauthorized to send message to this conversation"
    }
    ```
  </Accordion>

  <Accordion title="Conversation blocked">
    **Status Code**: `403`

    ```json theme={null}
    {
      "error": "Conversation is blocked"
    }
    ```
  </Accordion>

  <Accordion title="Image validation failed">
    **Status Code**: `400`

    ```json theme={null}
    {
      "error": "Image size exceeds 5MB limit"
    }
    ```
  </Accordion>
</AccordionGroup>

<Note>
  The chat system includes automatic notifications for new messages and supports both customer-to-store and customer-to-admin conversations with seamless transfer capabilities.
</Note>

<Tip>
  Use typing indicators sparingly to avoid overwhelming the system. Consider debouncing typing status updates.
</Tip>
