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.
Optional order ID if conversation is about a specific order
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
Type of message being sent
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
}
});
Mark Messages as Read
Mark messages as read in a conversation.
conversationId
Id<'conversations'>
required
Conversation ID
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" ]
});
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"
});
Delete Message
Delete a message (soft delete - marks as deleted but preserves for audit).
await convex . mutation ( api . shared . chat . deleteMessage , {
messageId: "msg123456789"
});
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
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
});
Get Conversations
Retrieve user’s conversations with pagination.
status
'active' | 'archived' | 'closed'
Filter by conversation status (default: active conversations)
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
Maximum number of messages to return (default: 50)
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 , {});
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.
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:
Direct messaging is enabled globally
Store has direct messaging enabled
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:
React Hook
Vue Composition API
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.