translate Available in: RU VI

QuotyAI Inboxes & Bridges

Omnichannel Inboxes & Bridges

QuotyAI uses a dual-channel architecture to handle customer conversations across every platform. Whether you want complete data ownership with Native Inboxes, or prefer to leverage your existing Chatwoot setup with Inbox Bridges, QuotyAI gives you the flexibility to choose the right approach for your business.


Two Ways to Handle Conversations

QuotyAI implements two distinct approaches for managing conversational channels:

  1. Native Inboxes: Channels where conversation history is stored natively within QuotyAI’s MongoDB database β€” you own the data completely.
  2. Inbox Bridges: Proxy connections to external Chatwoot instances β€” QuotyAI processes messages but conversation history lives externally.

Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Channel Layer                              β”‚
β”‚  Telegram β”‚ Facebook β”‚ WhatsApp β”‚ Instagram β”‚ Voice/LiveKit   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Native Inboxes β”‚    β”‚ Inbox Bridges  β”‚
β”‚ (Data Owner)  β”‚    β”‚ (Proxy Layer)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚                           β”‚
        β–Ό                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ QuotyAI DB   β”‚    β”‚ Chatwoot API  β”‚
β”‚ (Full Store)  β”‚    β”‚ (Ext. Store) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚                           β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚  Unified AI Processing       β”‚
        β”‚  (Sales + Management      β”‚
        β”‚   Assistants)             β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Native Inboxes: Complete Data Ownership

What Are Native Inboxes?

Native inboxes are communication channels where QuotyAI owns the complete conversation lifecycle. Every message, conversation thread, and contact profile is stored directly in your MongoDB database. You get full control, complete audit trails, and rich attachment processing with OCR.

Supported Channels

Channel Enum Value Status
Telegram TELEGRAM βœ… Active
Facebook Messenger FACEBOOK βœ… Active
Instagram INSTAGRAM βœ… Active
WhatsApp WHATSAPP βœ… Active
Voice (LiveKit) LIVEKIT βœ… Active

Data Storage Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  MongoDB (QuotyAI DB)                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ native-inboxes           β†’ Inbox configs & credentials    β”‚
β”‚ native-inbox-conversations β†’ Conversation threads          β”‚
β”‚ native-inbox-contacts    β†’ Unified contact profiles      β”‚
β”‚ native-inbox-telegram-messages β†’ Telegram messages     β”‚
β”‚ native-inbox-facebook-messages β†’ Facebook messages     β”‚
β”‚ voice-inboxes           β†’ Voice inbox configs          β”‚
β”‚ voice-inbox-sessions     β†’ Voice call sessions         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Core Data Models

NativeInboxDoc (Inbox Configuration)

export interface NativeInboxDoc {
  _id: ObjectId;
  tenantId: ObjectId;
  businessEntityId: ObjectId;  // Links to your business
  name: string;
  channel: NativeInboxChannelType;  // TELEGRAM, FACEBOOK, etc.
  status: NativeInboxStatus;  // 'active' | 'disabled' | 'disconnected'
  
  // Channel-specific credentials (embedded)
  credentials: NativeInboxChannelCredentialsDoc;
  // Telegram: { botToken, botUsername }
  // Facebook:  { pageId, accessToken, appSecret }
  // WhatsApp: { phoneNumberId, businessAccountId }
  
  webhookUrl: string;
  webhookSecret?: string;  // For verification
  
  // AI configuration
  defaultAiReplyAssistantAssignment?: ConversationAiAssignmentDoc;
  
  createdAt: Date;
  updatedAt?: Date;
  deletedAt?: Date;  // Soft delete
}

NativeInboxConversationDoc (Conversation Thread)

export interface NativeInboxConversationDoc {
  _id: ObjectId;
  tenantId: ObjectId;
  inboxId: ObjectId;
  contactId: ObjectId;
  channel: NativeInboxChannelType;
  
  // AI assignment (can override inbox-level default)
  aiReplyAssistantAssignment?: ConversationAiAssignmentDoc;
  
  customAttributes?: Record<string, unknown>;
  externalId: string;  // Telegram chat ID, Facebook PSID, etc.
  
  lastInboundMessage?: { text?: string; createdAt: Date };
  lastOutboundMessage?: { text?: string; createdAt: Date };
  
  createdAt: Date;
  updatedAt?: Date;
  deletedAt?: Date;
}

BaseNativeInboxMessageDoc (Message)

export interface BaseNativeInboxMessageDoc {
  _id: ObjectId;
  tenantId: ObjectId;
  conversationId: ObjectId;
  inboxId: ObjectId;
  
  direction: MessageDirection;  // 'inbound' | 'outbound'
  status: MessageStatusEnum;  // 'sent' | 'delivered' | 'read' | 'failed'
  text?: string;
  
  sender: NativeInboxMessageSenderDoc;
  // { id: string; type: 'customer' | 'ai' | 'human_agent' | 'system' }
  
  // Rich attachments (all stored with GCS URLs)
  attachedImages?: NativeInboxMessageImageAttachmentDoc[];     // + OCR data
  attachedVideos?: NativeInboxMessageVideoAttachmentDoc[];
  attachedAudios?: NativeInboxMessageAudioAttachmentDoc[];
  attachedFiles?: NativeInboxMessageFileAttachmentDoc[];
  attachedLocations?: NativeInboxMessageLocationAttachmentDoc[];
  attachedContacts?: NativeInboxMessageContactAttachmentDoc[];
  attachedStickers?: NativeInboxMessageStickerAttachmentDoc[];
  
  externalId?: string;  // Platform message ID
  metadata?: NativeInboxMessageMetadata;  // { runId for observability }
  
  visibleForCustomer: boolean;
  visibleForAi: boolean;
  
  createdAt: Date;
  deletedAt?: Date;
}

How Native Inboxes Work

Message Receiving Flow

Customer sends message
        ↓
Telegram/Facebook API sends webhook to QuotyAI
        ↓
Webhook Receiver (verify secret, check rate limits)
        ↓
Event Queue (priority-based, async processing)
        ↓
Conversation Workflow Service
        ↓
        β”œβ”€β†’ Find/Create Contact (unified profile)
        β”œβ”€β†’ Find/Create Conversation
        β”œβ”€β†’ Store Message (with OCR for images)
        └─→ Call AI Assistant
                ↓
        AI generates response
                ↓
        Send via Channel SDK (Telegram SDK, Facebook SDK)
                ↓
        Store outbound message in MongoDB

Event Queue System

The UnifiedEventQueueService provides robust asynchronous processing:

export enum QueuedEventType {
  MESSAGE = 'message',
  EDITED_MESSAGE = 'edited_message',
  CALLBACK_QUERY = 'callback_query',
  POSTBACK = 'postback',
  // ... 20+ event types supported
}

export enum EventPriority {
  HIGH = 0,    // User messages, payments
  NORMAL = 1,  // Delivery confirmations
  LOW = 2,     // Read receipts, reactions
}

Features:

  • Priority-based processing (HIGH β†’ NORMAL β†’ LOW)
  • Automatic retry with exponential backoff (max 3 retries)
  • Rate limiting per chat ID
  • Idempotency (prevents duplicate processing)
  • Concurrent processing with configurable limits

Key Services

Service Responsibility
TelegramChannelLifecycleService Inbox CRUD, bot token validation, webhook setup/delete
TelegramWebhookReceiverService Receive webhooks, verify secret, queue events
TelegramConversationWorkflowService Process messages, store in DB, manage contacts
FacebookChannelLifecycleService Same for Facebook/Instagram
UnifiedNativeInboxLifecycleService Unified read operations across all native inboxes
UnifiedNativeInboxConversationService Conversation search, pagination, AI assignment

Inbox Bridges: Proxy Architecture

What Are Inbox Bridges?

Inbox bridges are proxy connections to external Chatwoot instances. Unlike native inboxes, QuotyAI does NOT store conversation history. Instead, it proxies requests to the Chatwoot API in real-time while handling AI processing locally.

Key Concept: Proxy vs Storage

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    QuotyAI Stores                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ chatwoot-inbox-bridges β†’ Bridge configs only           β”‚
β”‚ external-chatwoot-accounts β†’ External account credentials β”‚
β”‚ external-chatwoot-reviewed-conversations β†’ Review markersβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β”‚ Proxies requests
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Chatwoot Instance                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Conversations (lives here)                            β”‚
β”‚ Messages (lives here)                                 β”‚
β”‚ Contacts (lives here)                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Supported via Chatwoot

Since Chatwoot supports 10+ channels, inbox bridges indirectly support:

  • Facebook Messenger, Instagram, WhatsApp
  • Email, Website chat widgets
  • Slack, Microsoft Teams
  • Any custom channel via API

Data Models

ChatwootInboxBridgeDoc (Bridge Configuration)

export interface ChatwootInboxBridgeDoc {
  _id: ObjectId;
  tenantId: ObjectId;
  name: string;
  isActive: boolean;
  
  // Reference to external Chatwoot account
  parentExternalAccountId?: ObjectId;
  
  // Chatwoot connection details
  chatwootHost?: string;              // e.g., "https://chatwoot.example.com"
  chatwootUserAdminToken?: string;     // Admin API token
  chatwootAccountId?: number;          // Numeric account ID
  chatwootAgentBotId?: number;         // Bot ID in Chatwoot
  chatwootAgentBotAccessToken?: string; // Bot access token
  
  // Inbox identification
  createdChatwootInboxId?: number;       // For internal QuotyAI Chatwoot
  latestConnectedChatwootInboxId?: number; // Last connected inbox
  
  // AI Assistant connection
  apiKeyId: ObjectId;                   // For webhook authentication
  assistantRole?: AssistantRole;          // 'sales' | 'accountant'
  assistantId?: ObjectId;                 // Reference to SalesAssistantDoc
  
  // URLs
  managementUrl?: string;   // Chatwoot management URL
  messengerUrl?: string;  // Public bot URL
  
  createdAt: Date;
  updatedAt?: Date;
  deletedAt?: Date;
}

ExternalChatwootAccountDoc (External Account)

export interface ExternalChatwootAccountDoc {
  _id: ObjectId;
  tenantId: ObjectId;
  name: string;
  host: string;                    // Chatwoot instance URL
  accountId: number;                // Chatwoot account ID
  userAdminAccessToken: string;     // Admin API token
  createdAt: Date;
  deletedAt?: Date;
}

How Inbox Bridges Work

Bridge Creation Flow

User creates bridge in QuotyAI
        ↓
ExternalChatwootService.createExternalChatwootInboxBridge()
        ↓
1. Get external account (host, token from QuotyAI DB)
2. Create MongoDB bridge document (get _id for webhook URL)
        ↓
3. Create agent bot in Chatwoot via API
   POST /api/v1/widget/bots
        ↓
4. Update bridge with access token & bot ID
5. Return bridge data to user

Webhook Processing (Incoming Messages)

When Chatwoot receives a message, it sends a webhook to QuotyAI:

// ChatwootWebhookProcessorService
async processWebhookEvent(
  payload: ChatwootWebhookPayload,
  tenantId: ObjectId,
  chatwootInboxBridge: ExternalChatwootInboxBridge
): Promise<void> {
  
  switch (payload.event) {
    case 'message_created':
      if (chatwootInboxBridge.assistantRole === 'sales') {
        await this.unifiedChatbotService.processIncomingMessage(
          InboxBridgeType.CHATWOOT,  // Channel type
          tenantId,
          assistantId,
          { 
            payload, 
            chatwootInboxBridge,
            attachments: [...] 
          },
          signal
        );
      }
      break;
    // ... handle other events
  }
}

Reading Conversations (Proxy Pattern)

// ChatwootConversationsService
async getExternalConversations(
  externalAccountId: string,
  tenantId: ObjectId,
  query: Record<string, unknown>
): Promise<Conversation[]> {
  
  // Get account credentials from QuotyAI DB
  const fullAccount = await this.getFullExternalChatwootAccountById(...);
    
  // Direct proxy to Chatwoot API (NOT from QuotyAI DB)
  const response = await conversationList({
    baseUrl: fullAccount.host,
    headers: { 'api_access_token': fullAccount.userAdminAccessToken },
    path: { account_id: fullAccount.accountId },
    query: query
  });
    
  return response.data?.data?.payload || [];
}

Marking Conversations as Reviewed

QuotyAI stores a marker in its own DB:

// ChatwootConversationsService.markConversationAsReviewed()
await this.reviewedConversationsCollection.insertOne({
  tenantId,
  businessEntityId: new ObjectId(dto.businessEntityId),
  accountId: dto.accountId,
  inboxId: dto.inboxId,
  conversationId: dto.conversationId,
  reviewerNotes: dto.reviewerNotes,
  reviewedAt: new Date(),
  createdAt: new Date()
});

Unified AI Processing

Both native inboxes and inbox bridges use the same AI processing pipeline through channel-specific adapters.

UnifiedSalesAssistantChatbotService

export class UnifiedSalesAssistantChatbotService {
  private readonly adapters: Map<UnifiedInboxChannelType, IChatAdapter>;
  
  constructor() {
    // Register adapters for each channel type
    this.adapters.set(InboxBridgeType.CHATWOOT, this.chatwootAdapter);
    this.adapters.set(NativeInboxChannelType.FACEBOOK, this.facebookAdapter);
    this.adapters.set(NativeInboxChannelType.TELEGRAM, this.telegramAdapter);
    this.adapters.set(StatelessApiChannelType.STATELESS_API, this.statelessApiAdapter);
  }
  
  async processIncomingMessage(
    channelType: UnifiedInboxChannelType,
    tenantId: ObjectId,
    assistantId: ObjectId,
    input: ChannelInput,  // Different for each channel
    abortSignal: AbortSignal
  ): Promise<ProcessedMessageResult> {
    
    const adapter = this.adapters.get(channelType);
    // Adapter handles:
    // 1. Building AI context (conversation history, etc.)
    // 2. Calling LLM
    // 3. Sending response back to the channel
  }
}

Channel Input Types

Different channels have different input structures:

// For Native Telegram Inbox
export interface TelegramChannelInput {
  tenantId: ObjectId;
  conversationId: string;
  message: string;
  chatId: string;
  senderId: string;
  inbox: NativeInboxDoc;
  inboxId: string;
}

// For Native Facebook Inbox
export interface FacebookChannelInput {
  tenantId: ObjectId;
  conversationId: string;
  message: string;
  pageId: string;
  psid: string;
  senderId: string;
  inbox: NativeInboxDoc;
  inboxId: string;
}

// For Chatwoot Bridge
export interface ChatwootChannelInput {
  tenantId: ObjectId;
  conversationId: number;  // Chatwoot conversation ID
  message: string;
  payload: ChatwootWebhookPayload;  // Full webhook payload
  chatwootInboxBridge: ExternalChatwootInboxBridge;
  attachments?: ChatwootAttachment[];
}

Integration with Business Entities

Both inbox types connect to business entities for AI context and configuration.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Business Entity                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ Pricing rules & formulas                         β”‚
β”‚ β€’ Service offerings & catalog                       β”‚
β”‚ β€’ Business facts (FAQs, policies)                  β”‚
β”‚ β€’ Instructions for AI assistants                    β”‚
β”‚ β€’ Configuration (timezone, currency, industry)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
        β”‚             β”‚
        β–Ό             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Sales        β”‚ β”‚ Management    β”‚
β”‚ Assistant   β”‚ β”‚ Assistant    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                 β”‚
       β–Ό                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Native       β”‚ β”‚ Inbox        β”‚
β”‚ Inboxes      β”‚ β”‚ Bridges      β”‚
β”‚ (Telegram,   β”‚ β”‚ (Chatwoot)   β”‚
β”‚  Facebook...) β”‚ β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

AI Assistant Assignment

Native Inbox level:

// NativeInboxDoc.defaultAiReplyAssistantAssignment
{
  mode: "fixed_assistant",  // or "dynamic_latest"
  assistantId: ObjectId,        // Reference to SalesAssistantDoc
  assistantRole: "sales",       // or "accountant"
  businessEntityId: ObjectId,
  sendingMessageMode: "ai_only",  // or "human_only", "ai_with_handover"
  handover?: { handedOverAt, handedOverBy, reason }
}

Conversation level (overrides inbox):

// NativeInboxConversationDoc.aiReplyAssistantAssignment
// Same structure - can override inbox-level assignment

Inbox Bridge level:

// ChatwootInboxBridgeDoc
{
  assistantRole: "sales",
  assistantId: ObjectId,
  // ... webhook URL uses apiKeyId for authentication
}

Feature Comparison

Feature Native Inbox Inbox Bridge (Chatwoot)
Data Ownership QuotyAI MongoDB External Chatwoot DB
Conversation Storage Native (MongoDB) External (Chatwoot API)
Message Retrieval From local DB Proxy to Chatwoot API
Webhook Handling Receive β†’ Store β†’ Process Receive β†’ Process (not stored)
Supported Channels Telegram, Facebook, Instagram, WhatsApp, Voice Any channel Chatwoot supports (10+)
Setup Complexity Direct API integration Requires Chatwoot instance
Attachment Storage GCS URLs + OCR Chatwoot handles storage
Reviewed Conversations N/A Markers stored in QuotyAI
Contact Unification Yes (across channels) Chatwoot handles contacts
Voice Support Yes (LiveKit) Via Chatwoot
AI Handover Native implementation Via Chatwoot UI + API
OCR Processing Built-in (images) Not available
Audit Trail Full (all in MongoDB) Limited (QuotyAI stores markers only)

Technical Implementation Details

Database Collections

Collection Purpose Stores
native-inboxes Inbox configurations tenantId, channel, credentials, webhookUrl, AI assignment
native-inbox-conversations Conversation threads tenantId, inboxId, contactId, externalId, AI assignment
native-inbox-contacts Unified contact profiles tenantId, name, email, channelIdentifiers[]
native-inbox-telegram-messages Telegram messages conversationId, text, events[], attachedImages[], OCR data
native-inbox-facebook-messages Facebook messages Same structure as Telegram
voice-inboxes Voice inbox configs tenantId, name, voiceSettings, assistantId
voice-inbox-sessions Voice call sessions voiceInboxId, roomName, transcript[], callStatus
chatwoot-inbox-bridges Bridge configurations host, accountId, agentBotId, access tokens, assistant assignments
external-chatwoot-accounts External account credentials host URL, accountId, admin access token
external-chatwoot-reviewed-conversations Review markers accountId, inboxId, conversationId, reviewer notes

Service Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    API Layer (Hono)                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ /omnichannel/inbox-lifecycles/* β†’ Inbox CRUD routes      β”‚
β”‚ /omnichannel/conversations/* β†’ Conversation routes         β”‚
β”‚ /omnichannel/webhooks/* β†’ Webhook endpoints             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
        β”‚             β”‚
        β–Ό             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Native       β”‚ β”‚ Chatwoot     β”‚
β”‚ Services     β”‚ β”‚ Services     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Telegram     β”‚ β”‚ External-    β”‚
β”‚ Facebook    β”‚ β”‚ Chatwoot     β”‚
β”‚ Lifecycle    β”‚ β”‚ Service      β”‚
β”‚ Workflow    β”‚ β”‚ Bridge       β”‚
β”‚ Webhook     β”‚ β”‚ Conversationsβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚             β”‚
        β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Unified AI Processing                       β”‚
β”‚        UnifiedSalesAssistantChatbotService              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ Channel Adapters (Chatwoot, Facebook, Telegram)    β”‚
β”‚ β€’ Skills System (8 modular skills with tools)          β”‚
β”‚ β€’ Dynamic Runner (Sandboxed TypeScript execution)      β”‚
β”‚ β€’ Observability (Full audit trails)                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Configuration Example: Creating a Native Telegram Inbox

// POST /omnichannel/inbox-lifecycles/telegram/inboxes/
const response = await fetch('/api/omnichannel/inbox-lifecycles/telegram/inboxes/', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    telegramApiToken: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11',
    name: 'My Telegram Bot',
    businessEntityId: '507f1f77bcf86cd799439011',
    apiKeyId: '507f1f77bcf86cd799439012'
  })
});

// Response includes:
// - Inbox ID
// - Bot info (id, username)
// - Webhook status
// - Auto-created business entity & assistant (if requested)

Configuration Example: Creating a Chatwoot Inbox Bridge

// POST /omnichannel/inbox-lifecycles/chatwoot/external-instance/inbox-bridges/
const response = await fetch('/api/omnichannel/inbox-lifecycles/chatwoot/external-instance/inbox-bridges/', {
  method: 'POST',
  body: JSON.stringify({
    name: 'My Chatwoot Bridge',
    parentExternalAccountId: '507f1f77bcf86cd799439013',
    assistantRole: 'sales',
    assistantId: '507f1f77bcf86cd799439014',
    apiKeyId: '507f1f77bcf86cd799439015'
  })
});

// This:
// 1. Creates MongoDB bridge document
// 2. Creates agent bot in Chatwoot
// 3. Configures webhook URL
// QuotyAI does NOT store conversations - only bridge config

Reading Conversations: Native vs Bridge

// Native Inbox: From local MongoDB
// POST /omnichannel/conversations/native-inbox-conversations/search
const nativeConv = await fetch('/api/omnichannel/conversations/native-inbox-conversations/search', {
  method: 'POST',
  body: JSON.stringify({
    page: 1,
    limit: 20,
    channel: 'TELEGRAM',
    sort: 'lastMessageAt',
    order: 'desc'
  })
});

// Inbox Bridge: Proxied from Chatwoot API
// GET /omnichannel/conversations/chatwoot-bridge-conversations/conversations/:bridgeId
const bridgeConv = await fetch(`/api/omnichannel/conversations/chatwoot-bridge-conversations/conversations/${bridgeId}?limit=20`);
// This calls Chatwoot API internally and returns conversations

When to Use Which?

Choose Native Inboxes When You Want:

  • βœ… Complete data ownership β€” all data in your MongoDB
  • βœ… Direct channel integration β€” no middleware
  • βœ… Rich attachment processing β€” OCR, image analysis
  • βœ… Unified contact profiles across channels
  • βœ… Voice/chat convergence β€” same assistant handles both
  • βœ… Full audit trails β€” every message in your DB
  • βœ… Custom workflows β€” full control over processing

Best for: Businesses that want full control, need OCR processing, or handle sensitive data.

Choose Inbox Bridges When You:

  • βœ… Already use Chatwoot β€” leverage existing setup
  • βœ… Need broader channel support β€” Chatwoot supports 10+ channels
  • βœ… Want Chatwoot’s UI/features β€” conversation management, agent interface
  • βœ… Prefer not to duplicate data β€” conversations stay in Chatwoot
  • βœ… Have complex routing needs β€” Chatwoot’s advanced routing rules

Best for: Businesses already on Chatwoot, or those needing channels QuotyAI doesn’t natively support yet.


The Power of Unified AI

Both approaches provide the same AI assistant capabilities through the unified UnifiedSalesAssistantChatbotService, ensuring consistent customer experience regardless of the underlying inbox type.

Customer sends message via Telegram
        ↓
Native Inbox (stores message)
        ↓                    OR
Customer sends message via Chatwoot
        ↓
Inbox Bridge (proxies to Chatwoot)
        ↓
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚  Same AI Processing Pipeline         β”‚
        β”‚  β€’ 80+ languages                  β”‚
        β”‚  β€’ 1-2 second response time       β”‚
        β”‚  β€’ Deterministic pricing           β”‚
        β”‚  β€’ Handover to human agents     β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        ↓
Customer receives consistent, high-quality response

Summary

QuotyAI’s dual inbox architecture gives you the flexibility to choose the right approach:

Consideration Recommendation
Data sovereignty matters Use Native Inboxes
Already on Chatwoot Use Inbox Bridges
Need OCR/attachment processing Use Native Inboxes
Need 10+ channels fast Use Inbox Bridges
Voice + chat convergence Use Native Inboxes
Want simple setup Either β€” both are straightforward

Both approaches are first-class citizens in QuotyAI, with full AI assistant integration, business entity support, and comprehensive observability.