GET
/
api
/
public
/
contact
/
v2
/
{aiContactId}
/
messages
Retrieve messages by AI contact with pagination
curl --request GET \
  --url https://api.vambe.me/api/public/contact/v2/{aiContactId}/messages \
  --header 'x-api-key: <x-api-key>'

Overview

Retrieve messages from a contact with pagination support. This endpoint returns a flat list of messages in chronological order, perfect for processing large message histories efficiently. This is the recommended endpoint for accessing message history when you need pagination, sequential processing, or want to handle large datasets.

Use Cases

  • Message Processing: Process messages sequentially for analytics or exports
  • Large Message Histories: Handle contacts with thousands of messages efficiently
  • Data Export: Export all messages in chunks
  • Message Analytics: Analyze message patterns, response times, sentiment
  • Search & Filter: Build custom message search and filtering
  • Audit Logs: Create audit trails of all communications

Authentication

This endpoint requires authentication using an API key. Include your API key in the request header:
x-api-key: your_api_key_here

Path Parameters

ParameterTypeRequiredDescription
aiContactIdstring (UUID)YesUnique identifier of the contact

Query Parameters

ParameterTypeRequiredDescription
pagenumberYesPage number to retrieve (starts at 1)
Pagination: Each page returns a fixed number of messages. Keep incrementing page until you receive an empty array.

Response Structure

Returns an object with pagination information and messages:
FieldTypeDescription
messagesarrayArray of message objects for this page
pagenumberCurrent page number
hasMorebooleanWhether there are more pages available
totalnumberTotal number of messages (optional)

Message Object

FieldTypeDescription
idstring (UUID)Unique message identifier
bodystringMessage content/text
created_atstring (ISO)Message timestamp
directionstring”INBOUND” or “OUTBOUND”
fromstringSender identifier (phone/username)
tostringRecipient identifier
typestringMessage type (text, image, video, audio, etc.)
statusstringDelivery status (sent, delivered, read, failed)
media_urlstring | nullURL to media file (if applicable)
client_idstring (UUID)Your organization ID
ai_contact_idstring (UUID)Associated contact ID

Example Requests

Get First Page

curl --request GET \
  'https://api.vambe.ai/api/public/contact/v2/df980fc8-b6db-4820-bf22-2969482d106d/messages?page=1' \
  --header 'x-api-key: your_api_key_here'

Get Second Page

curl --request GET \
  'https://api.vambe.ai/api/public/contact/v2/df980fc8-b6db-4820-bf22-2969482d106d/messages?page=2' \
  --header 'x-api-key: your_api_key_here'

Example Response

{
  "messages": [
    {
      "id": "msg-001",
      "body": "Hola! Necesito ayuda con mi pedido",
      "created_at": "2024-09-30T10:00:00.000Z",
      "direction": "INBOUND",
      "from": "+56912345678",
      "to": "+56987654321",
      "type": "text",
      "status": "delivered",
      "media_url": null,
      "client_id": "550e8400-e29b-41d4-a716-446655440000",
      "ai_contact_id": "df980fc8-b6db-4820-bf22-2969482d106d"
    },
    {
      "id": "msg-002",
      "body": "Por supuesto! ¿Cuál es tu número de pedido?",
      "created_at": "2024-09-30T10:01:30.000Z",
      "direction": "OUTBOUND",
      "from": "+56987654321",
      "to": "+56912345678",
      "type": "text",
      "status": "read",
      "media_url": null,
      "client_id": "550e8400-e29b-41d4-a716-446655440000",
      "ai_contact_id": "df980fc8-b6db-4820-bf22-2969482d106d"
    }
  ],
  "page": 1,
  "hasMore": true,
  "total": 127
}

Common Use Cases

1. Fetch All Messages with Pagination

const getAllMessages = async (contactId) => {
  let allMessages = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `https://api.vambe.ai/api/public/contact/v2/${contactId}/messages?page=${page}`,
      {
        headers: {
          'x-api-key': 'your_api_key_here',
        },
      },
    );

    const data = await response.json();
    allMessages = allMessages.concat(data.messages);
    hasMore = data.hasMore;
    page++;

    console.log(
      `Fetched page ${page - 1}, total messages: ${allMessages.length}`,
    );
  }

  console.log(`Retrieved all ${allMessages.length} messages`);
  return allMessages;
};

2. Export Messages to CSV

const exportMessagesToCSV = async (contactId) => {
  const allMessages = await getAllMessages(contactId);

  const csvRows = [
    'Timestamp,Direction,From,To,Message,Type,Status',
    ...allMessages.map((msg) => {
      const timestamp = new Date(msg.created_at).toISOString();
      const message = msg.body.replace(/"/g, '""'); // Escape quotes
      return `"${timestamp}","${msg.direction}","${msg.from}","${msg.to}","${message}","${msg.type}","${msg.status}"`;
    }),
  ];

  const csvContent = csvRows.join('\n');
  console.log('CSV export ready');

  return csvContent;
};

3. Calculate Response Time Metrics

const calculateResponseMetrics = async (contactId) => {
  const allMessages = await getAllMessages(contactId);

  const responseTimes = [];

  for (let i = 0; i < allMessages.length - 1; i++) {
    const current = allMessages[i];
    const next = allMessages[i + 1];

    // If customer message followed by agent message
    if (current.direction === 'INBOUND' && next.direction === 'OUTBOUND') {
      const responseTime =
        new Date(next.created_at) - new Date(current.created_at);
      responseTimes.push(responseTime);
    }
  }

  const averageResponseTime =
    responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length;

  const metrics = {
    totalMessages: allMessages.length,
    inboundMessages: allMessages.filter((m) => m.direction === 'INBOUND')
      .length,
    outboundMessages: allMessages.filter((m) => m.direction === 'OUTBOUND')
      .length,
    averageResponseTimeMinutes: (averageResponseTime / 1000 / 60).toFixed(2),
    fastestResponseMinutes: (Math.min(...responseTimes) / 1000 / 60).toFixed(2),
    slowestResponseMinutes: (Math.max(...responseTimes) / 1000 / 60).toFixed(2),
  };

  console.log('Response Metrics:', metrics);
  return metrics;
};

4. Find Messages with Specific Content

const findMessagesContaining = async (contactId, searchTerm) => {
  const allMessages = await getAllMessages(contactId);

  const matches = allMessages.filter((msg) =>
    msg.body.toLowerCase().includes(searchTerm.toLowerCase()),
  );

  console.log(`Found ${matches.length} messages containing "${searchTerm}"`);

  return matches;
};

// Usage
const orderMentions = await findMessagesContaining('contact-id', 'order');
const urgentMessages = await findMessagesContaining('contact-id', 'urgent');

5. Get Latest Messages Only

const getLatestMessages = async (contactId, limit = 20) => {
  const response = await fetch(
    `https://api.vambe.ai/api/public/contact/v2/${contactId}/messages?page=1`,
    {
      headers: {
        'x-api-key': 'your_api_key_here',
      },
    },
  );

  const data = await response.json();
  const latestMessages = data.messages.slice(0, limit);

  console.log(`Retrieved ${latestMessages.length} latest messages`);
  return latestMessages;
};

Pagination Details

Page Size

  • Each page typically returns 20-50 messages (exact number may vary)
  • Pages are numbered starting from 1
  • Messages are ordered chronologically (oldest to newest or newest to oldest)

Fetching All Pages

// Helper function to fetch all pages
const fetchAllPages = async (contactId) => {
  let currentPage = 1;
  let allMessages = [];
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `https://api.vambe.ai/api/public/contact/v2/${contactId}/messages?page=${currentPage}`,
      {
        headers: { 'x-api-key': 'your_api_key_here' },
      },
    );

    const data = await response.json();

    if (data.messages.length === 0) {
      hasMore = false;
    } else {
      allMessages.push(...data.messages);
      currentPage++;
    }
  }

  return allMessages;
};

Error Responses

Status CodeDescription
400Bad Request - Invalid page parameter
401Unauthorized - Invalid or missing API key
404Not Found - Contact not found
500Internal Server Error - Something went wrong

Important Notes

  • Pagination Required: The page parameter is mandatory
  • Page Starts at 1: First page is 1, not 0
  • No Total Count: Response may not include total message count
  • Check hasMore: Use hasMore field to determine if more pages exist
  • Cross-Channel: Works with all communication channels
  • Include Stage History: Messages include stage change events

When to Use This vs Conversations

RequirementUse This Endpoint (Messages v2)Use Conversations
Need pagination✅ Yes❌ No
Large message history✅ Yes❌ No
Flat message list✅ Yes❌ No
Sequential processing✅ Yes❌ No
Chat UI with threads❌ No✅ Yes
Conversation view❌ No✅ Yes
Human-readable format❌ No✅ Yes
Filter by recent daysUse paginationdaysBack parameter

Best Practices

1. Handle Empty Pages

const getMessagesPage = async (contactId, page) => {
  const response = await fetch(
    `https://api.vambe.ai/api/public/contact/v2/${contactId}/messages?page=${page}`,
    {
      headers: { 'x-api-key': 'your_api_key_here' },
    },
  );

  const data = await response.json();

  if (data.messages.length === 0) {
    console.log('No more messages');
    return null;
  }

  return data;
};

2. Implement Rate Limiting

const fetchWithDelay = async (contactId, maxPages) => {
  const allMessages = [];

  for (let page = 1; page <= maxPages; page++) {
    const data = await getMessagesPage(contactId, page);

    if (!data || data.messages.length === 0) break;

    allMessages.push(...data.messages);

    // Add delay to avoid rate limiting
    await new Promise((resolve) => setTimeout(resolve, 100));
  }

  return allMessages;
};

3. Cache Pages

const messageCache = new Map();

const getCachedMessagesPage = async (contactId, page) => {
  const cacheKey = `${contactId}-${page}`;

  if (messageCache.has(cacheKey)) {
    return messageCache.get(cacheKey);
  }

  const response = await fetch(
    `https://api.vambe.ai/api/public/contact/v2/${contactId}/messages?page=${page}`,
    {
      headers: { 'x-api-key': 'your_api_key_here' },
    },
  );

  const data = await response.json();
  messageCache.set(cacheKey, data);

  return data;
};

Headers

x-api-key
string
required

API key required to authorize the request

Path Parameters

aiContactId
string
required

ID of the AI contact

Query Parameters

page
number
required

Page number

Response

Messages retrieved successfully.