Skip to main content

Overview

Once storage is enabled, the SDK provides powerful methods to query and analyze your WhatsApp messages. This guide covers all available query methods with practical examples.
Make sure you’ve completed the Supabase Integration setup before using these methods.

Get Conversation History

Retrieve all messages from a conversation with a specific phone number.

Method Signature

client.getConversation(
  phoneNumber: string,
  options?: {
    limit?: number;
    offset?: number;
    dateFrom?: Date;
    dateTo?: Date;
  }
): Promise<ConversationResult>

Basic Usage

// Get last 50 messages
const conversation = await client.getConversation('+1234567890', {
  limit: 50,
  offset: 0
});

console.log(`Total messages: ${conversation.totalMessages}`);
console.log(`Returned: ${conversation.messages.length}`);

// Display messages
conversation.messages.forEach(msg => {
  const direction = msg.direction === 'incoming' ? '📩' : '📤';
  const content = msg.content.text || `[${msg.message_type}]`;
  console.log(`${direction} ${content}`);
});

With Date Range

// Get messages from last 7 days
const lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);

const recentConversation = await client.getConversation('+1234567890', {
  dateFrom: lastWeek,
  dateTo: new Date(),
  limit: 100
});

console.log(`Messages from last week: ${recentConversation.messages.length}`);

Pagination

async function getAllMessages(phoneNumber: string) {
  const allMessages = [];
  const pageSize = 100;
  let offset = 0;
  let hasMore = true;

  while (hasMore) {
    const result = await client.getConversation(phoneNumber, {
      limit: pageSize,
      offset: offset
    });

    allMessages.push(...result.messages);
    offset += pageSize;
    hasMore = result.messages.length === pageSize;
  }

  return allMessages;
}

// Usage
const allMessages = await getAllMessages('+1234567890');
console.log(`Total messages retrieved: ${allMessages.length}`);

Response Structure

interface ConversationResult {
  phoneNumber: string;
  totalMessages: number;
  messages: Array<{
    id: string;
    whatsappMessageId: string;
    fromPhone: string;
    toPhone: string;
    message_type: string;
    content: {
      text?: string;
      caption?: string;
      // ... other fields based on type
    };
    direction: 'incoming' | 'outgoing';
    status: 'sent' | 'delivered' | 'read' | 'failed';
    timestamp: Date;
    metadata: Record<string, any>;
  }>;
}

Search Messages

Search for messages containing specific text across all conversations or within a specific conversation.

Method Signature

client.searchMessages(options: {
  text: string;
  phoneNumber?: string;
  messageType?: string;
  dateFrom?: Date;
  dateTo?: Date;
  limit?: number;
  offset?: number;
}): Promise<SearchResult>
// Search for "order" across all conversations
const results = await client.searchMessages({
  text: 'order',
  limit: 20
});

console.log(`Found ${results.total} messages containing "order"`);

results.messages.forEach(msg => {
  console.log(`From: ${msg.fromPhone}`);
  console.log(`Text: ${msg.content.text}`);
  console.log(`Date: ${msg.timestamp}`);
  console.log('---');
});

Search Within Conversation

// Search for "invoice" in a specific conversation
const invoiceMessages = await client.searchMessages({
  text: 'invoice',
  phoneNumber: '+1234567890',
  limit: 50
});

console.log(`Found ${invoiceMessages.total} invoice messages`);

Search by Message Type

// Find all image messages containing "receipt"
const receiptImages = await client.searchMessages({
  text: 'receipt',
  messageType: 'image',
  limit: 10
});

// Find all document messages
const documents = await client.searchMessages({
  text: '', // Empty string matches all
  messageType: 'document',
  limit: 100
});

Search with Date Range

// Search for "payment" in messages from January 2024
const januaryPayments = await client.searchMessages({
  text: 'payment',
  dateFrom: new Date('2024-01-01'),
  dateTo: new Date('2024-01-31'),
  limit: 50
});

Advanced Search Example

// Search for order-related messages in the last month
async function searchRecentOrders(customerPhone: string) {
  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

  const results = await client.searchMessages({
    text: 'ORDER-', // Matches ORDER-12345, ORDER-67890, etc.
    phoneNumber: customerPhone,
    dateFrom: thirtyDaysAgo,
    limit: 100
  });

  // Extract order numbers from messages
  const orderNumbers = results.messages
    .map(msg => {
      const match = msg.content.text?.match(/ORDER-\d+/);
      return match ? match[0] : null;
    })
    .filter(Boolean);

  return [...new Set(orderNumbers)]; // Remove duplicates
}

// Usage
const orders = await searchRecentOrders('+1234567890');
console.log('Recent orders:', orders);

Get Message Thread

Retrieve all replies to a specific message, useful for tracking conversation context.

Method Signature

client.getMessageThread(
  messageId: string
): Promise<MessageThread | null>

Basic Usage

// Get thread for a specific message
const thread = await client.getMessageThread('message_id_here');

if (thread) {
  console.log('Original message:', thread.originalMessage.content.text);
  console.log(`Replies: ${thread.replies.length}`);

  thread.replies.forEach((reply, index) => {
    console.log(`Reply ${index + 1}: ${reply.content.text}`);
  });
} else {
  console.log('No thread found');
}

Track Support Tickets

async function getSupportThreadDetails(ticketMessageId: string) {
  const thread = await client.getMessageThread(ticketMessageId);

  if (!thread) {
    return null;
  }

  return {
    originalIssue: thread.originalMessage.content.text,
    customerPhone: thread.originalMessage.fromPhone,
    totalReplies: thread.replies.length,
    resolved: thread.replies.some(r =>
      r.content.text?.toLowerCase().includes('resolved')
    ),
    lastReply: thread.replies[thread.replies.length - 1],
    timeline: thread.replies.map(r => ({
      from: r.fromPhone,
      text: r.content.text,
      timestamp: r.timestamp
    }))
  };
}

// Usage
const ticketDetails = await getSupportThreadDetails('msg_123');
console.log('Support ticket:', ticketDetails);

Get Conversation Analytics

Analyze conversation patterns, response rates, and engagement metrics.

Method Signature

client.getConversationAnalytics(
  phoneNumber: string,
  options?: {
    from?: Date;
    to?: Date;
  }
): Promise<ConversationAnalytics>

Basic Usage

const analytics = await client.getConversationAnalytics('+1234567890', {
  from: new Date('2024-01-01'),
  to: new Date()
});

console.log('Analytics:', {
  totalMessages: analytics.totalMessages,
  incomingMessages: analytics.incomingMessages,
  outgoingMessages: analytics.outgoingMessages,
  averageResponseTime: analytics.averageResponseTime,
  responseRate: analytics.responseRate
});

Response Structure

interface ConversationAnalytics {
  phoneNumber: string;
  totalMessages: number;
  incomingMessages: number;
  outgoingMessages: number;
  firstMessageAt: Date;
  lastMessageAt: Date;
  averageResponseTime: number; // in milliseconds
  responseRate: number; // percentage (0-100)
  messagesByType: Record<string, number>;
  messagesByDay: Array<{
    date: string;
    count: number;
  }>;
}

Calculate Customer Satisfaction

async function calculateCustomerSatisfaction(phoneNumber: string) {
  const analytics = await client.getConversationAnalytics(phoneNumber);

  // Fast response time (< 5 minutes) = good satisfaction
  const avgResponseMinutes = analytics.averageResponseTime / 1000 / 60;

  let satisfaction: 'Excellent' | 'Good' | 'Fair' | 'Poor';

  if (avgResponseMinutes < 5) {
    satisfaction = 'Excellent';
  } else if (avgResponseMinutes < 30) {
    satisfaction = 'Good';
  } else if (avgResponseMinutes < 120) {
    satisfaction = 'Fair';
  } else {
    satisfaction = 'Poor';
  }

  return {
    satisfaction,
    avgResponseMinutes: Math.round(avgResponseMinutes),
    totalConversations: analytics.totalMessages,
    responseRate: analytics.responseRate
  };
}

// Usage
const satisfaction = await calculateCustomerSatisfaction('+1234567890');
console.log('Customer satisfaction:', satisfaction);

Team Performance Dashboard

async function getTeamPerformance(customerPhones: string[]) {
  const performanceData = await Promise.all(
    customerPhones.map(async (phone) => {
      const analytics = await client.getConversationAnalytics(phone, {
        from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days
      });

      return {
        customer: phone,
        messages: analytics.totalMessages,
        responseTime: analytics.averageResponseTime,
        responseRate: analytics.responseRate
      };
    })
  );

  // Calculate team averages
  const teamAvg = {
    totalMessages: performanceData.reduce((sum, p) => sum + p.messages, 0),
    avgResponseTime: performanceData.reduce((sum, p) => sum + p.responseTime, 0) / performanceData.length,
    avgResponseRate: performanceData.reduce((sum, p) => sum + p.responseRate, 0) / performanceData.length
  };

  return {
    individual: performanceData,
    team: teamAvg
  };
}

Export Conversation

Export conversation data for backup, compliance, or analysis purposes.

Method Signature

client.exportConversation(
  phoneNumber: string,
  options?: {
    format?: 'json' | 'csv';
    dateFrom?: Date;
    dateTo?: Date;
  }
): Promise<ExportResult>

JSON Export

// Export as JSON
const jsonExport = await client.exportConversation('+1234567890', {
  format: 'json'
});

console.log('Export data:', jsonExport.data);
console.log('Message count:', jsonExport.messageCount);

// Save to file
import fs from 'fs';
fs.writeFileSync(
  'conversation-export.json',
  JSON.stringify(jsonExport.data, null, 2)
);

CSV Export

// Export as CSV
const csvExport = await client.exportConversation('+1234567890', {
  format: 'csv'
});

// CSV string ready for download
console.log(csvExport.data);

// Save to file
import fs from 'fs';
fs.writeFileSync('conversation-export.csv', csvExport.data);

Export with Date Range

// Export Q1 2024 messages
const q1Export = await client.exportConversation('+1234567890', {
  format: 'json',
  dateFrom: new Date('2024-01-01'),
  dateTo: new Date('2024-03-31')
});

console.log(`Exported ${q1Export.messageCount} messages from Q1 2024`);

Automated Daily Backups

async function dailyBackup(customerPhones: string[]) {
  const today = new Date().toISOString().split('T')[0];

  for (const phone of customerPhones) {
    const export = await client.exportConversation(phone, {
      format: 'json',
      dateFrom: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24 hours
      dateTo: new Date()
    });

    if (export.messageCount > 0) {
      const filename = `backup-${phone}-${today}.json`;
      fs.writeFileSync(filename, JSON.stringify(export.data));
      console.log(`✅ Backed up ${export.messageCount} messages to ${filename}`);
    }
  }
}

// Run daily
dailyBackup(['+1234567890', '+0987654321']);

Advanced Query Patterns

async function searchAcrossCustomers(
  customerPhones: string[],
  searchText: string
) {
  const results = await Promise.all(
    customerPhones.map(async (phone) => {
      const messages = await client.searchMessages({
        text: searchText,
        phoneNumber: phone,
        limit: 10
      });

      return {
        phone,
        matchCount: messages.total,
        matches: messages.messages
      };
    })
  );

  // Filter customers with matches
  return results.filter(r => r.matchCount > 0);
}

// Usage
const matches = await searchAcrossCustomers(
  ['+1234567890', '+0987654321'],
  'urgent'
);

Time-Based Analysis

async function getHourlyMessageDistribution(phoneNumber: string) {
  const conversation = await client.getConversation(phoneNumber, {
    dateFrom: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days
  });

  const hourlyDistribution = Array(24).fill(0);

  conversation.messages.forEach(msg => {
    const hour = new Date(msg.timestamp).getHours();
    hourlyDistribution[hour]++;
  });

  return hourlyDistribution.map((count, hour) => ({
    hour: `${hour}:00`,
    messageCount: count
  }));
}

// Usage
const distribution = await getHourlyMessageDistribution('+1234567890');
console.log('Peak hours:', distribution.filter(h => h.messageCount > 10));

Customer Segmentation

async function segmentCustomersByActivity(customerPhones: string[]) {
  const segments = {
    active: [],      // > 50 messages in last 30 days
    moderate: [],    // 10-50 messages
    inactive: []     // < 10 messages
  };

  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

  for (const phone of customerPhones) {
    const analytics = await client.getConversationAnalytics(phone, {
      from: thirtyDaysAgo
    });

    if (analytics.totalMessages > 50) {
      segments.active.push(phone);
    } else if (analytics.totalMessages >= 10) {
      segments.moderate.push(phone);
    } else {
      segments.inactive.push(phone);
    }
  }

  return segments;
}

Performance Tips

1. Use Pagination

Always paginate large result sets:
// Good
const page1 = await client.getConversation(phone, { limit: 50, offset: 0 });
const page2 = await client.getConversation(phone, { limit: 50, offset: 50 });

// Avoid
const all = await client.getConversation(phone, { limit: 10000 });

2. Filter by Date

Narrow down queries with date ranges:
// Good - specific date range
const recent = await client.searchMessages({
  text: 'order',
  dateFrom: new Date('2024-01-01'),
  dateTo: new Date('2024-01-31')
});

// Avoid - searching all history
const all = await client.searchMessages({ text: 'order' });

3. Cache Frequent Queries

const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function getCachedAnalytics(phoneNumber: string) {
  const cached = cache.get(phoneNumber);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  const analytics = await client.getConversationAnalytics(phoneNumber);

  cache.set(phoneNumber, {
    data: analytics,
    timestamp: Date.now()
  });

  return analytics;
}

Next Steps

I