Skip to content

YeboShops Messaging System Documentation

Overview

YeboShops implements a two-tier messaging system designed for marketplace/classified interactions. Similar to Facebook Pages, users can message shops (not other users directly), and shop owners manage these conversations through their shop identity.

Architecture

Core Principles

  1. Shop-Centric Communication: All messages flow through shops, never directly between users
  2. Dual Identity Management: Shop owners can act as themselves or as their shop
  3. Notification Routing: Messages to shops trigger notifications to shop owners
  4. Message Attribution: Clear distinction between user messages and shop responses

Data Models

Chat Model

typescript
interface IChat {
  participants: string (UUID)[];  // [userId, shopOwnerId]
  shop: string (UUID);            // Required - the shop being messaged
  lastProduct?: string (UUID);    // Optional - last discussed product
  createdAt: Date;
  updatedAt: Date;
}

Message Model

typescript
interface IMessage {
  chatId: string (UUID);
  senderId: string (UUID);        // User ID or Shop ID
  senderType: 'User' | 'Shop';
  content: string;
  productContext?: IProductContext;
  attachments?: IMessageAttachment[];
  replyToMessageId?: string (UUID);
  read: boolean;
  readAt?: Date;
  createdAt: Date;
}

Notification Model

Messages sent to shops create notifications for shop owners:

typescript
{
  user: string (UUID);           // Shop owner's user ID
  type: 'chat';
  title: string;            // "New message from [username]"
  message: string;          // Message preview (50 chars)
  data: {
    chatId: string;
  }
}

Message Flow

1. User Initiates Conversation with Shop

mermaid
sequenceDiagram
    participant User
    participant API
    participant Shop
    participant ShopOwner

    User->>API: POST /chats
    Note right of User: {shopId, content, productId?}
    API->>API: Create/Find Chat
    API->>API: Create Message (senderType: 'User')
    API->>Shop: Associate message with shop
    API->>ShopOwner: Send notification
    API-->>User: Return message & chatId

Endpoint: POST /chats/messages

json
{
  "shopId": "shop_123",
  "content": "Is this item still available?",
  "productId": "product_456"  // Optional
}

2. Shop Owner Views Messages

mermaid
sequenceDiagram
    participant ShopOwner
    participant API
    participant Database

    ShopOwner->>API: GET /chats/user/:userId
    API->>Database: Find chats where user participates
    API->>Database: Find chats for user's shops
    Database-->>API: Return combined chats
    API->>API: Mark with isShopOwner flag
    API-->>ShopOwner: Return enriched chats

Endpoint: GET /chats/user/:userId or GET /chats/with-unread-counts

Response includes:

  • Chats where user is a participant
  • Chats for shops owned by the user
  • isShopOwner: true flag for shop-owned chats

3. Shop Owner Replies to Message

mermaid
sequenceDiagram
    participant ShopOwner
    participant API
    participant User

    ShopOwner->>API: POST /chats
    Note right of ShopOwner: {chatId, content}
    API->>API: Check if sender owns shop
    API->>API: Create Message (senderType: 'Shop')
    API->>API: Set senderId to shop ID
    API-->>ShopOwner: Return message
    API->>User: Message appears from shop

Endpoint: POST /chats

json
{
  "chatId": "chat_789",
  "content": "Yes, this item is available!"
}

API Endpoints

Core Messaging Endpoints

Send Message

http
POST /chats
Authorization: Bearer {token}

{
  "shopId": "string",        // Required for new chat
  "chatId": "string",        // Required for existing chat
  "content": "string",       // Required
  "productId": "string",     // Optional
  "replyToMessageId": "string"  // Optional
}

Alternative Shop Message Route

http
POST /chats/shop/:shopId
Authorization: Bearer {token}

{
  "content": "string",
  "productId": "string",
  "replyToMessageId": "string"
}

Get User's Chats

http
GET /chats/user/:userId
Authorization: Bearer {token}

Returns all chats including those for shops owned by the user.

Get Enhanced Chats with Unread Counts

http
GET /chats/with-unread-counts
Authorization: Bearer {token}

Query params:
- limit: number (1-50, default: 20)
- before: ISO timestamp (for pagination)
- includeTotalCount: boolean

Get Messages from Chat

http
GET /chats/:chatId/messages
Authorization: Bearer {token}

Query params:
- limit: number (1-100, default: 30)
- before: ISO timestamp (pagination cursor)
- after: ISO timestamp (reverse pagination)

Check if Chat Exists

http
POST /chats/check-exists
Authorization: Bearer {token}

{
  "userId": "string",
  "shopId": "string"
}

Or use the GET alternative:

http
GET /chats/shop/:shopId/check-exists
Authorization: Bearer {token}

Read Receipt Management

Mark Messages as Read

http
POST /chats/:chatId/read
Authorization: Bearer {token}

Get Unread Count for Chat

http
GET /chats/:chatId/unread-count
Authorization: Bearer {token}

Get All Unread Counts

http
GET /chats/unread-counts
Authorization: Bearer {token}

Additional Features

Get Last Product from Chat

http
GET /chats/:chatId/last-product
Authorization: Bearer {token}

Get Chat Details

http
GET /chats/:chatId
Authorization: Bearer {token}

Business Rules

Message Creation Rules

  1. Chat Initialization

    • Only users can initiate chats with shops
    • Shop owners cannot start conversations (must wait for user contact)
    • Each user-shop pair has only one chat
  2. Sender Identification

    • Regular users: senderType: 'User', senderId: userId
    • Shop owners replying: senderType: 'Shop', senderId: shopId
    • System automatically detects sender type based on chat ownership
  3. Validation Rules

    • Cannot message yourself
    • Cannot message non-existent shops
    • Must be a chat participant to send messages
    • Either chatId or shopId required (not both)

Notification Rules

  1. Recipient Routing

    • Messages to shops → Notifications to shop owner's user account
    • No notifications for own messages
    • De-duplication within 5-minute window
  2. Notification Content

    • Title: "New message from [username]"
    • Body: First 50 characters of message
    • Data: Includes chatId for navigation

Chat Retrieval Rules

  1. Visibility

    • Users see chats where they are participants
    • Shop owners additionally see all chats for their shops
    • Chats marked with isShopOwner flag when applicable
  2. Ordering

    • Chats sorted by most recent activity
    • Messages sorted chronologically within chats

Security Considerations

Authentication & Authorization

  • All endpoints require authentication via Bearer token
  • Users can only access their own chats
  • Shop owners have elevated access to shop-related chats

Data Privacy

  • Messages are private between user and shop
  • No public message visibility
  • Read receipts only visible to participants

Rate Limiting Recommendations

  • Message sending: 30 messages/minute per user
  • Chat creation: 10 new chats/hour per user
  • File uploads: 50MB max per attachment

Implementation Notes

Product Context

Messages can include product context when discussing specific items:

typescript
interface IProductContext {
  productId: string;
  title: string;
  image: string;
  mediaType: 'image' | 'video';
  price: string;
  url: string;
}

Attachment Support

Messages support image and video attachments:

typescript
interface IMessageAttachment {
  type: 'image' | 'video';
  url: string;
  key: string;  // R2 storage key
  filename: string;
  size?: number;
  mimeType?: string;
}

Pagination

The system uses cursor-based pagination with ISO timestamps:

  • before: Get older messages
  • after: Get newer messages
  • Returns nextCursor, prevCursor, hasMore, hasPrevious

Real-time Considerations

While not currently implemented, the system is designed to support:

  • WebSocket connections for real-time messaging
  • Push notifications for mobile apps
  • Online/offline status tracking

Error Handling

Common error responses:

ErrorStatus CodeMessage
Missing shopId/chatId400"Either chatId or shopId must be provided"
Shop not found404"Shop not found"
Not a participant403"User is not a participant in this chat"
Self-messaging400"Shop owners cannot initiate conversations"
Invalid string (UUID)400"Invalid ID format"

Testing Checklist

  • [ ] User can message a shop
  • [ ] Shop owner receives notification
  • [ ] Shop owner sees chat in their list
  • [ ] Shop owner can reply as shop
  • [ ] User sees shop's reply
  • [ ] Cannot message users directly
  • [ ] Cannot self-message
  • [ ] Read receipts work correctly
  • [ ] Unread counts are accurate
  • [ ] Pagination works in both directions
  • [ ] Product context is preserved
  • [ ] Attachments upload successfully

Migration Notes

For existing systems migrating to this architecture:

  1. Existing User-to-User Chats: Should be deprecated or migrated to shop-based chats
  2. Notification System: Update to route through user IDs, not shop IDs
  3. Frontend Updates: Ensure UI distinguishes between user and shop messages
  4. Permission System: Verify shop owners have proper access to their shop's chats

Support & Maintenance

Monitoring Points

  • Message delivery success rate
  • Notification delivery rate
  • Average response time
  • Chat creation rate
  • Unread message accumulation

Common Issues & Solutions

Issue: Shop owner not receiving notifications

  • Check shop ownership in database
  • Verify notification service is using owner's userId
  • Check notification de-duplication window

Issue: Messages appearing as user instead of shop

  • Verify shop ownership
  • Check senderType logic in message creation
  • Ensure chatId is provided for replies

Issue: Missing chats for shop owner

  • Verify shop ownership records
  • Check query includes shop-owned chats
  • Ensure proper indexing on shop field

One chat. Everything done.