> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vambe.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Retrieve messages by AI contact with pagination

> Replaces the old endpoint /public/ai-message/:aiContactId

## 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

| Parameter     | Type          | Required | Description                      |
| ------------- | ------------- | -------- | -------------------------------- |
| `aiContactId` | string (UUID) | Yes      | Unique identifier of the contact |

## Query Parameters

| Parameter | Type   | Required | Description                           |
| --------- | ------ | -------- | ------------------------------------- |
| `page`    | number | Yes      | Page number to retrieve (starts at 1) |

<Note>
  **Pagination**: Each page returns a fixed number of messages. Keep
  incrementing `page` until you receive an empty array.
</Note>

## Response Structure

Returns an object with pagination information and messages:

| Field      | Type    | Description                            |
| ---------- | ------- | -------------------------------------- |
| `messages` | array   | Array of message objects for this page |
| `page`     | number  | Current page number                    |
| `hasMore`  | boolean | Whether there are more pages available |
| `total`    | number  | Total number of messages (optional)    |

### Message Object

| Field           | Type           | Description                                     |
| --------------- | -------------- | ----------------------------------------------- |
| `id`            | string (UUID)  | Unique message identifier                       |
| `body`          | string         | Message content/text                            |
| `created_at`    | string (ISO)   | Message timestamp                               |
| `direction`     | string         | "INBOUND" or "OUTBOUND"                         |
| `from`          | string         | Sender identifier (phone/username)              |
| `to`            | string         | Recipient identifier                            |
| `type`          | string         | Message type (text, image, video, audio, etc.)  |
| `status`        | string         | Delivery status (sent, delivered, read, failed) |
| `media_url`     | string \| null | URL to media file (if applicable)               |
| `client_id`     | string (UUID)  | Your organization ID                            |
| `ai_contact_id` | string (UUID)  | Associated contact ID                           |

## Example Requests

### Get First Page

```bash theme={null}
curl --request GET \
  'https://api.vambe.me/api/public/contact/v2/df980fc8-b6db-4820-bf22-2969482d106d/messages?page=1' \
  --header 'x-api-key: your_api_key_here'
```

### Get Second Page

```bash theme={null}
curl --request GET \
  'https://api.vambe.me/api/public/contact/v2/df980fc8-b6db-4820-bf22-2969482d106d/messages?page=2' \
  --header 'x-api-key: your_api_key_here'
```

## Example Response

```json theme={null}
{
  "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

```javascript theme={null}
const getAllMessages = async (contactId) => {
  let allMessages = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `https://api.vambe.me/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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
const getLatestMessages = async (contactId, limit = 20) => {
  const response = await fetch(
    `https://api.vambe.me/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

```javascript theme={null}
// 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.me/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 Code | Description                                  |
| ----------- | -------------------------------------------- |
| 400         | Bad Request - Invalid page parameter         |
| 401         | Unauthorized - Invalid or missing API key    |
| 404         | Not Found - Contact not found                |
| 500         | Internal 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

| Requirement               | Use 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 days** | Use pagination                  | ✅ `daysBack` parameter |

## Related Endpoints

* [GET /api/public/contact/{aiContactId}/conversations](/reference/contact/get-conversations) - Get conversations grouped (better for chat UI)
* [GET /api/public/contact/{aiContactId}/info](/reference/contact/get-info-of-an-ai-contact) - Get contact info
* [GET /api/public/contacts/search](/reference/contact/search-contacts) - Search contacts by phone/email

## Best Practices

### 1. Handle Empty Pages

```javascript theme={null}
const getMessagesPage = async (contactId, page) => {
  const response = await fetch(
    `https://api.vambe.me/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

```javascript theme={null}
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

```javascript theme={null}
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.me/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;
};
```


## OpenAPI

````yaml get /api/public/contact/v2/{aiContactId}/messages
openapi: 3.0.0
info:
  title: Vambe AI API
  description: Vambe AI documentation
  version: '1.0'
  contact: {}
servers:
  - url: https://api.vambe.me
    description: Production Server
security: []
tags:
  - name: Vambe AI
    description: ''
paths:
  /api/public/contact/v2/{aiContactId}/messages:
    get:
      tags:
        - Contact
      summary: Retrieve messages by AI contact with pagination
      description: Replaces the old endpoint /public/ai-message/:aiContactId
      operationId: PublicAiContactController_getMessagesV2
      parameters:
        - name: x-api-key
          in: header
          description: API key required to authorize the request
          required: true
          schema:
            type: string
        - name: aiContactId
          required: true
          in: path
          description: ID of the AI contact
          schema:
            type: string
        - name: page
          required: true
          in: query
          description: Page number
          schema:
            type: number
      responses:
        '200':
          description: Messages retrieved successfully.
        '404':
          description: AI contact not found.

````