> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/No-Country-simulation/S02-26-Equipo-33-Web-App/llms.txt
> Use this file to discover all available pages before exploring further.

# Messages

> Send and retrieve messages within conversations

## Overview

Messages are individual chat messages within a conversation. The Horse Trust platform supports both REST API and Socket.io for sending and receiving messages in real-time.

## Authentication

All message endpoints require authentication via JWT token.

**REST API:**

```
Authorization: Bearer <your_jwt_token>
```

**Socket.io:**

```javascript theme={null}
const socket = io('https://api.horsetrust.com', {
  auth: {
    token: '<your_jwt_token>'
  }
});
```

***

## Get Messages

<RequestExample>
  ```bash cURL theme={null}
  curl https://api.horsetrust.com/api/chat/conversations/65a1b2c3d4e5f6a7b8c9d0e1/messages?page=1&limit=30 \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const conversationId = '65a1b2c3d4e5f6a7b8c9d0e1';
  const response = await fetch(
    `https://api.horsetrust.com/api/chat/conversations/${conversationId}/messages?page=1&limit=30`,
    {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );

  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests

  conversation_id = '65a1b2c3d4e5f6a7b8c9d0e1'
  response = requests.get(
      f'https://api.horsetrust.com/api/chat/conversations/{conversation_id}/messages',
      headers={'Authorization': f'Bearer {token}'},
      params={'page': 1, 'limit': 30}
  )

  data = response.json()
  ```
</RequestExample>

<ResponseExample>
  ```json Success Response theme={null}
  {
    "success": true,
    "data": [
      {
        "_id": "65a1b2c3d4e5f6a7b8c9d0e3",
        "conversation_id": "65a1b2c3d4e5f6a7b8c9d0e1",
        "sender_id": {
          "_id": "507f1f77bcf86cd799439010",
          "full_name": "John Smith",
          "profile_picture_url": "https://cdn.horsetrust.com/users/john.jpg"
        },
        "text": "Is this horse still available?",
        "is_read": true,
        "read_at": "2024-03-15T10:32:00.000Z",
        "sent_at": "2024-03-15T10:30:00.000Z"
      },
      {
        "_id": "65a1b2c3d4e5f6a7b8c9d0e4",
        "conversation_id": "65a1b2c3d4e5f6a7b8c9d0e1",
        "sender_id": {
          "_id": "507f1f77bcf86cd799439011",
          "full_name": "Jane Doe",
          "profile_picture_url": "https://cdn.horsetrust.com/users/jane.jpg"
        },
        "text": "Yes, Thunder is still available! Would you like to schedule a visit?",
        "is_read": true,
        "read_at": "2024-03-15T10:36:00.000Z",
        "sent_at": "2024-03-15T10:35:00.000Z"
      }
    ]
  }
  ```

  ```json Error Response theme={null}
  {
    "success": false,
    "message": "Conversation not found"
  }
  ```
</ResponseExample>

### GET `/api/chat/conversations/:id/messages`

Retrieve paginated message history for a conversation. Messages are returned in chronological order (oldest first). When you fetch messages, all unread messages from other users are automatically marked as read.

<ParamField path="id" type="string" required>
  The conversation ID to retrieve messages from
</ParamField>

<ParamField query="page" type="string" default="1">
  Page number for pagination (minimum: 1)
</ParamField>

<ParamField query="limit" type="string" default="30">
  Number of messages per page (maximum: 100)
</ParamField>

### Response Fields

<ResponseField name="success" type="boolean" required>
  Indicates if the request was successful
</ResponseField>

<ResponseField name="data" type="array">
  Array of message objects in chronological order (oldest first)

  <ResponseField name="_id" type="string">
    Unique message identifier
  </ResponseField>

  <ResponseField name="conversation_id" type="string">
    ID of the conversation this message belongs to
  </ResponseField>

  <ResponseField name="sender_id" type="object">
    Populated sender user object

    <ResponseField name="_id" type="string">
      User's unique identifier
    </ResponseField>

    <ResponseField name="full_name" type="string">
      User's full name
    </ResponseField>

    <ResponseField name="profile_picture_url" type="string">
      URL to the user's profile picture
    </ResponseField>
  </ResponseField>

  <ResponseField name="text" type="string">
    Message content (maximum 2000 characters)
  </ResponseField>

  <ResponseField name="is_read" type="boolean">
    Whether the message has been read by the recipient
  </ResponseField>

  <ResponseField name="read_at" type="string">
    ISO 8601 timestamp when the message was read (null if unread)
  </ResponseField>

  <ResponseField name="sent_at" type="string">
    ISO 8601 timestamp when the message was sent
  </ResponseField>
</ResponseField>

<ResponseField name="message" type="string">
  Error message (only present when success is false)
</ResponseField>

<Note>
  Fetching messages automatically marks unread messages from other participants as read and sets the `read_at` timestamp.
</Note>

### Error Responses

* **404 Not Found**: Conversation not found or user is not a participant
* **500 Internal Server Error**: Server error occurred

***

## Send Message (REST)

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.horsetrust.com/api/chat/conversations/65a1b2c3d4e5f6a7b8c9d0e1/messages \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "text": "Yes, I would love to schedule a visit!"
    }'
  ```

  ```javascript JavaScript theme={null}
  const conversationId = '65a1b2c3d4e5f6a7b8c9d0e1';
  const response = await fetch(
    `https://api.horsetrust.com/api/chat/conversations/${conversationId}/messages`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        text: 'Yes, I would love to schedule a visit!'
      })
    }
  );

  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests

  conversation_id = '65a1b2c3d4e5f6a7b8c9d0e1'
  response = requests.post(
      f'https://api.horsetrust.com/api/chat/conversations/{conversation_id}/messages',
      headers={
          'Authorization': f'Bearer {token}',
          'Content-Type': 'application/json'
      },
      json={'text': 'Yes, I would love to schedule a visit!'}
  )

  data = response.json()
  ```
</RequestExample>

<ResponseExample>
  ```json Success Response theme={null}
  {
    "success": true,
    "data": {
      "_id": "65a1b2c3d4e5f6a7b8c9d0e5",
      "conversation_id": "65a1b2c3d4e5f6a7b8c9d0e1",
      "sender_id": "507f1f77bcf86cd799439010",
      "text": "Yes, I would love to schedule a visit!",
      "is_read": false,
      "sent_at": "2024-03-15T10:40:00.000Z"
    }
  }
  ```

  ```json Error Response theme={null}
  {
    "success": false,
    "message": "Conversation not found"
  }
  ```
</ResponseExample>

### POST `/api/chat/conversations/:id/messages`

Send a message to a conversation via REST API. This endpoint serves as a fallback when Socket.io is not available. The conversation's `last_message` field is automatically updated.

<ParamField path="id" type="string" required>
  The conversation ID to send the message to
</ParamField>

<ParamField body="text" type="string" required>
  The message content (maximum 2000 characters)
</ParamField>

### Response Fields

<ResponseField name="success" type="boolean" required>
  Indicates if the request was successful
</ResponseField>

<ResponseField name="data" type="object">
  The created message object

  <ResponseField name="_id" type="string">
    Unique message identifier
  </ResponseField>

  <ResponseField name="conversation_id" type="string">
    ID of the conversation
  </ResponseField>

  <ResponseField name="sender_id" type="string">
    ObjectId of the authenticated user who sent the message
  </ResponseField>

  <ResponseField name="text" type="string">
    Message content
  </ResponseField>

  <ResponseField name="is_read" type="boolean">
    Always `false` for newly created messages
  </ResponseField>

  <ResponseField name="sent_at" type="string">
    ISO 8601 timestamp when the message was sent
  </ResponseField>
</ResponseField>

<ResponseField name="message" type="string">
  Error message (only present when success is false)
</ResponseField>

### Error Responses

* **404 Not Found**: Conversation not found or user is not a participant
* **500 Internal Server Error**: Server error occurred

***

## Socket.io Real-Time Messaging

### Connection Setup

```javascript theme={null}
import { io } from 'socket.io-client';

const socket = io('https://api.horsetrust.com', {
  auth: {
    token: yourJwtToken
  }
});

socket.on('connect', () => {
  console.log('Connected to Socket.io');
});

socket.on('connect_error', (error) => {
  console.error('Connection error:', error.message);
});
```

<Note>
  Socket.io authentication uses JWT tokens passed in the `auth.token` field during connection. Invalid or missing tokens will result in a connection error.
</Note>

### Join Conversation Room

Before receiving real-time messages, join the conversation room:

```javascript theme={null}
const conversationId = '65a1b2c3d4e5f6a7b8c9d0e1';
socket.emit('join_conversation', conversationId);
```

<ParamField body="conversationId" type="string" required>
  The ID of the conversation to join
</ParamField>

### Send Message

Send a message via Socket.io with acknowledgment:

```javascript theme={null}
const messageData = {
  conversation_id: '65a1b2c3d4e5f6a7b8c9d0e1',
  text: 'Hello, is this horse still available?'
};

socket.emit('send_message', messageData, (response) => {
  if (response.success) {
    console.log('Message sent:', response.data);
  } else {
    console.error('Error:', response.message);
  }
});
```

**Event:** `send_message`

<ParamField body="conversation_id" type="string" required>
  The ID of the conversation to send the message to
</ParamField>

<ParamField body="text" type="string" required>
  The message content (maximum 2000 characters)
</ParamField>

**Acknowledgment Response:**

```json theme={null}
{
  "success": true,
  "data": {
    "_id": "65a1b2c3d4e5f6a7b8c9d0e5",
    "conversation_id": "65a1b2c3d4e5f6a7b8c9d0e1",
    "sender_id": {
      "_id": "507f1f77bcf86cd799439010",
      "full_name": "John Smith",
      "profile_picture_url": "https://cdn.horsetrust.com/users/john.jpg"
    },
    "text": "Hello, is this horse still available?",
    "is_read": false,
    "sent_at": "2024-03-15T10:40:00.000Z"
  }
}
```

### Receive New Messages

Listen for new messages in conversations you've joined:

```javascript theme={null}
socket.on('new_message', (message) => {
  console.log('New message received:', message);
  // Update your UI with the new message
});
```

**Event Payload:**

```json theme={null}
{
  "_id": "65a1b2c3d4e5f6a7b8c9d0e5",
  "conversation_id": "65a1b2c3d4e5f6a7b8c9d0e1",
  "sender_id": {
    "_id": "507f1f77bcf86cd799439011",
    "full_name": "Jane Doe",
    "profile_picture_url": "https://cdn.horsetrust.com/users/jane.jpg"
  },
  "text": "Yes, it is! Would you like to schedule a visit?",
  "is_read": false,
  "sent_at": "2024-03-15T10:45:00.000Z"
}
```

<Note>
  The `new_message` event is broadcast to all users in the conversation room, including the sender.
</Note>

### Message Notifications

Receive notifications when you receive a new message (delivered to your personal user room):

```javascript theme={null}
socket.on('message_notification', (notification) => {
  console.log('New message notification:', notification);
  // Show a notification badge or alert
});
```

**Event Payload:**

```json theme={null}
{
  "conversation_id": "65a1b2c3d4e5f6a7b8c9d0e1",
  "sender": "507f1f77bcf86cd799439011",
  "preview": "Yes, it is! Would you like to schedule a visit?"
}
```

<ResponseField name="conversation_id" type="string">
  ID of the conversation where the message was sent
</ResponseField>

<ResponseField name="sender" type="string">
  ObjectId of the user who sent the message
</ResponseField>

<ResponseField name="preview" type="string">
  First 60 characters of the message text
</ResponseField>

### Typing Indicators

Send and receive typing indicators:

```javascript theme={null}
// Send typing indicator
const conversationId = '65a1b2c3d4e5f6a7b8c9d0e1';
socket.emit('typing', conversationId);

// Stop typing indicator
socket.emit('stop_typing', conversationId);

// Receive typing indicators from others
socket.on('user_typing', (data) => {
  console.log(`User ${data.userId} is typing...`);
});

socket.on('user_stop_typing', (data) => {
  console.log(`User ${data.userId} stopped typing`);
});
```

**Typing Event Payloads:**

```json theme={null}
{
  "userId": "507f1f77bcf86cd799439011"
}
```

***

## Data Model

### Message Schema

```javascript theme={null}
{
  conversation_id: ObjectId,  // Required, indexed
  sender_id: ObjectId,        // Required
  text: String,               // Required, max 2000 chars
  is_read: Boolean,           // Default: false
  read_at: Date,              // Null until read
  deleted_at: Date,           // Soft delete timestamp
  sent_at: Date               // Auto-generated timestamp
}
```

### Validation Rules

* `text` field is required and limited to 2000 characters
* `conversation_id` must reference a valid conversation
* `sender_id` must be a participant in the conversation
* Messages are soft-deleted (using `deleted_at`) and excluded from queries

### Indexes

* `conversation_id`: Used for efficiently querying messages in a conversation

### Automatic Behavior

* When messages are fetched via GET endpoint, unread messages from other users are automatically marked as read
* When a message is sent, the conversation's `last_message` field is updated
* The `sent_at` timestamp is automatically set on creation
* Messages with `deleted_at` set are excluded from all queries

***

## Complete Example

### Real-time Chat Implementation

```javascript theme={null}
import { io } from 'socket.io-client';

// Initialize Socket.io connection
const socket = io('https://api.horsetrust.com', {
  auth: { token: localStorage.getItem('jwt_token') }
});

// Handle connection
socket.on('connect', () => {
  console.log('Connected to chat');
  
  // Join conversation
  const conversationId = '65a1b2c3d4e5f6a7b8c9d0e1';
  socket.emit('join_conversation', conversationId);
  
  // Load message history via REST API
  loadMessageHistory(conversationId);
});

// Listen for new messages
socket.on('new_message', (message) => {
  displayMessage(message);
});

// Listen for notifications
socket.on('message_notification', (notification) => {
  updateUnreadBadge();
  showNotification(notification.preview);
});

// Send message
function sendMessage(conversationId, text) {
  socket.emit('send_message', 
    { conversation_id: conversationId, text },
    (response) => {
      if (!response.success) {
        console.error('Failed to send:', response.message);
        // Fallback to REST API
        sendMessageViaREST(conversationId, text);
      }
    }
  );
}

// Typing indicators
let typingTimeout;
function handleTyping(conversationId) {
  socket.emit('typing', conversationId);
  
  clearTimeout(typingTimeout);
  typingTimeout = setTimeout(() => {
    socket.emit('stop_typing', conversationId);
  }, 1000);
}

// Load history via REST
async function loadMessageHistory(conversationId) {
  const response = await fetch(
    `https://api.horsetrust.com/api/chat/conversations/${conversationId}/messages?page=1&limit=50`,
    {
      headers: {
        'Authorization': `Bearer ${localStorage.getItem('jwt_token')}`
      }
    }
  );
  
  const data = await response.json();
  if (data.success) {
    data.data.forEach(displayMessage);
  }
}

// Fallback to REST API
async function sendMessageViaREST(conversationId, text) {
  const response = await fetch(
    `https://api.horsetrust.com/api/chat/conversations/${conversationId}/messages`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${localStorage.getItem('jwt_token')}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ text })
    }
  );
  
  const data = await response.json();
  if (data.success) {
    displayMessage(data.data);
  }
}
```
