Skip to main content

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

Real-time Messaging

Instant messaging with typing indicators and read receipts

Image Support

Send images with automatic validation and compression

Conversation Management

Create, archive, and manage conversations between users

Admin Support

Built-in admin support system with conversation transfer

Create or Get Conversation

Create a new conversation or get existing one for customer support.
orderId
Id<'orders'>
Optional order ID if conversation is about a specific order
storeId
Id<'stores'>
Optional store ID for direct store messaging
const result = await convex.mutation(api.shared.chat.createOrGetConversation, {
  orderId: "order123456789",
  storeId: "store123456789"
});
{
  "conversationId": "conv123456789",
  "conversationType": "customer_to_store"
}
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

Send Message

Send a text or image message in a conversation.
conversationId
Id<'conversations'>
required
Conversation ID to send message to
content
string
required
Message content
messageType
'text' | 'image'
required
Type of message being sent
imageAttachment
object
Image attachment details (required for image messages)
// 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
  }
});
"msg123456789"

Mark Messages as Read

Mark messages as read in a conversation.
conversationId
Id<'conversations'>
required
Conversation ID
messageIds
Array<Id<'messages'>>
Specific message IDs to mark as read (optional - marks all unread if not provided)
// 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"]
});
{
  "markedCount": 3
}

Archive Conversation

Archive a conversation to remove it from active conversations.
conversationId
Id<'conversations'>
required
Conversation ID to archive
await convex.mutation(api.shared.chat.archiveConversation, {
  conversationId: "conv123456789"
});
null

Delete Message

Delete a message (soft delete - marks as deleted but preserves for audit).
messageId
Id<'messages'>
required
Message ID to delete
await convex.mutation(api.shared.chat.deleteMessage, {
  messageId: "msg123456789"
});
null
Only the message sender can delete their own messages. Admins can delete any message.

Update Typing Status

Update user’s typing status in a conversation for real-time indicators.
conversationId
Id<'conversations'>
required
Conversation ID
isTyping
boolean
required
Whether user is currently typing
// 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
});
null

Get Conversations

Retrieve user’s conversations with pagination.
status
'active' | 'archived' | 'closed'
Filter by conversation status (default: active conversations)
limit
number
Maximum number of conversations to return (default: 50)
const conversations = await convex.query(api.shared.chat.getConversations, {
  status: "active",
  limit: 25
});
[
  {
    "_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"
  }
]

Get Conversation Details

Get detailed information about a specific conversation.
conversationId
Id<'conversations'>
required
Conversation ID to get details for
const conversation = await convex.query(api.shared.chat.getConversation, {
  conversationId: "conv123456789"
});
{
  "_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
}

Get Messages

Retrieve messages from a conversation with pagination.
conversationId
Id<'conversations'>
required
Conversation ID to get messages from
limit
number
Maximum number of messages to return (default: 50)
cursor
string
Pagination cursor for loading more messages
const result = await convex.query(api.shared.chat.getMessages, {
  conversationId: "conv123456789",
  limit: 25
});
{
  "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
}

Get Unread Count

Get total unread message count for the authenticated user.
const result = await convex.query(api.shared.chat.getUnreadCount, {});
{
  "totalUnread": 5
}

Get Typing Indicators

Get current typing indicators for a conversation.
conversationId
Id<'conversations'>
required
Conversation ID to get typing indicators for
const indicators = await convex.query(api.shared.chat.getTypingIndicators, {
  conversationId: "conv123456789"
});
[
  {
    "userId": "user123456789",
    "userType": "store",
    "userName": "Pizza Palace Support",
    "lastTypingAt": 1640996000000
  }
]

Can Chat with Store

Check if a customer can start a direct conversation with a store.
storeId
Id<'stores'>
required
Store ID to check chat eligibility for
const result = await convex.query(api.shared.chat.canChatWithStore, {
  storeId: "store123456789"
});
{
  "canChat": true,
  "reason": undefined,
  "orderIds": ["order123456789", "order987654321"]
}
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

Get Chat Settings

Get current chat system settings.
const settings = await convex.query(api.shared.chat.getChatSettings, {});
{
  "directMessagingEnabled": true,
  "maxImageSizeMB": 5,
  "allowedImageTypes": ["image/jpeg", "image/png", "image/gif", "image/webp"],
  "adminSupportEnabled": true
}

Real-time Integration

The chat system supports real-time updates through Convex subscriptions:
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
  };
}

Message Types

The system supports various message types:

Text Messages

Standard text messages with full UTF-8 support

Image Messages

Images with automatic validation and compression

System Messages

Automated system notifications and updates

Transfer Notices

Messages indicating conversation transfers

Error Handling

Status Code: 404
{
  "error": "Conversation not found"
}
Status Code: 403
{
  "error": "Unauthorized to send message to this conversation"
}
Status Code: 403
{
  "error": "Conversation is blocked"
}
Status Code: 400
{
  "error": "Image size exceeds 5MB limit"
}
The chat system includes automatic notifications for new messages and supports both customer-to-store and customer-to-admin conversations with seamless transfer capabilities.
Use typing indicators sparingly to avoid overwhelming the system. Consider debouncing typing status updates.