# Messages API

Full CRUD for messages within channels, plus threads, reactions, file uploads, and AI-powered summarization. All endpoints require JWT authentication and are rate-limited to 60 requests per minute.

Base path: `/api/v2/messages`

---

## POST /api/v2/messages

Create a new message in a channel and broadcast to all members.

Create a new message in a channel. The message is validated, inserted into the database, broadcast to all channel members via WebSocket, and forwarded to any assigned digital twins.

**Body Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `channel_id` | string | Yes | UUID of the channel to post in. |
| `content` | string | Yes | Message content. Supports markdown. |
| `content_type` | string | No | Content type identifier. Default: `text` |
| `parent_message_id` | string | No | UUID of parent message for threading. |
| `metadata` | object | No | Arbitrary metadata object attached to the message. |
| `attachments` | object[] | No | Array of attachment objects. |
| `conversation_id` | string | No | UUID of the conversation context. |
| `user_id` | string | No | Explicit user ID. Falls back to JWT user. |
| `ai_twin_id` | string | No | UUID of an AI twin sending this message. |

**Example Request**

```bash
curl -X POST https://api.lvng.ai/api/v2/messages \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Deploy the staging environment and run integration tests.",
    "metadata": {
      "source": "api",
      "priority": "high"
    }
  }'
```

**Response -- 201**

```json
{
  "success": true,
  "data": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Deploy the staging environment and run integration tests.",
    "content_type": "text",
    "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "parent_message_id": null,
    "metadata": {
      "source": "api",
      "priority": "high"
    },
    "attachments": null,
    "created_at": "2026-03-19T15:10:22.000Z",
    "updated_at": "2026-03-19T15:10:22.000Z"
  },
  "meta": {
    "timestamp": "2026-03-19T15:10:22.000Z"
  }
}
```

---

## POST /api/v2/messages/send

Alias for POST /api/v2/messages. Same body and response.

**Body Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `channel_id` | string | Yes | UUID of the channel. |
| `content` | string | Yes | Message content. |

**Example Request**

```bash
curl -X POST https://api.lvng.ai/api/v2/messages/send \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Quick update: tests passed."
  }'
```

**Response -- 201**

```json
{
  "success": true,
  "data": {
    "id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Quick update: tests passed.",
    "content_type": "text",
    "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "created_at": "2026-03-19T15:12:00.000Z",
    "updated_at": "2026-03-19T15:12:00.000Z"
  },
  "meta": {
    "timestamp": "2026-03-19T15:12:00.000Z"
  }
}
```

---

## GET /api/v2/messages/history

List messages in a channel with pagination. Includes user profiles and reactions.

Retrieve message history for a channel with offset-based pagination. Messages are returned with LEFT JOIN data from user_profiles and reaction counts.

**Query Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `channel_id` | string | Yes | UUID of the channel to fetch messages from. |
| `thread_id` | string | No | Filter to messages in a specific thread. |
| `limit` | integer | No | Maximum number of messages to return. Default: `50` |
| `offset` | integer | No | Number of messages to skip. Default: `0` |

**Example Request**

```bash
curl -X GET "https://api.lvng.ai/api/v2/messages/history?channel_id=550e8400-e29b-41d4-a716-446655440000&limit=25&offset=0" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

**Response -- 200**

```json
{
  "success": true,
  "data": [
    {
      "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "channel_id": "550e8400-e29b-41d4-a716-446655440000",
      "content": "Can you analyze the Q1 marketing data?",
      "content_type": "text",
      "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
      "display_name": "Matty Squarzoni",
      "avatar_url": "https://storage.lvng.ai/avatars/a0eebc99.jpg",
      "reactions": [],
      "parent_message_id": null,
      "metadata": {},
      "created_at": "2026-03-19T14:30:00.000Z",
      "updated_at": "2026-03-19T14:30:00.000Z"
    }
  ],
  "meta": {
    "total": 142,
    "page": 1,
    "limit": 25,
    "offset": 0
  }
}
```

---

## GET /api/v2/messages/search

Full-text search across messages. Searches the content column.

**Query Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `query` | string | Yes | Search term. Matched against messages.content. |
| `channel_id` | string | No | Scope search to a specific channel. |
| `limit` | integer | No | Maximum results to return. |

**Example Request**

```bash
curl -X GET "https://api.lvng.ai/api/v2/messages/search?query=deployment&channel_id=550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

**Response -- 200**

```json
{
  "success": true,
  "data": [
    {
      "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "channel_id": "550e8400-e29b-41d4-a716-446655440000",
      "content": "Deploy the staging environment and run integration tests.",
      "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
      "created_at": "2026-03-19T15:10:22.000Z"
    }
  ],
  "meta": {
    "total": 1
  }
}
```

---

## GET /api/v2/messages/:id

Get a single message by UUID. Includes reaction count.

**Path Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | UUID of the message. |

**Example Request**

```bash
curl -X GET https://api.lvng.ai/api/v2/messages/7c9e6679-7425-40de-944b-e07fc1f90ae7 \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

**Response -- 200**

```json
{
  "success": true,
  "data": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Can you analyze the Q1 marketing data?",
    "content_type": "text",
    "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "parent_message_id": null,
    "metadata": {},
    "reaction_count": 3,
    "created_at": "2026-03-19T14:30:00.000Z",
    "updated_at": "2026-03-19T14:30:00.000Z"
  },
  "meta": {}
}
```

---

## PUT /api/v2/messages/:id

Edit a message. Checks ownership before updating.

Update the content of an existing message. Ownership is checked -- only the original author can edit.

**Path Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | UUID of the message to edit. |

**Body Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `user_id` | string | Yes | UUID of the user making the edit. Must match original author. |
| `content` | string | Yes | Updated message content. |

**Example Request**

```bash
curl -X PUT https://api.lvng.ai/api/v2/messages/7c9e6679-7425-40de-944b-e07fc1f90ae7 \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "content": "Can you analyze the Q1 and Q2 marketing data?"
  }'
```

**Response -- 200**

```json
{
  "success": true,
  "data": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Can you analyze the Q1 and Q2 marketing data?",
    "content_type": "text",
    "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "metadata": {},
    "created_at": "2026-03-19T14:30:00.000Z",
    "updated_at": "2026-03-19T15:45:00.000Z"
  }
}
```

---

## DELETE /api/v2/messages/:id

Soft delete a message. Sets deleted_at timestamp rather than removing the row.

Admins can delete any message; regular users can only delete their own.

**Path Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | UUID of the message to delete. |

**Query Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `user_id` | string | Yes | UUID of the user requesting deletion. |
| `admin` | boolean | No | Set to true to bypass ownership check (requires admin role). |

**Example Request**

```bash
curl -X DELETE "https://api.lvng.ai/api/v2/messages/7c9e6679-7425-40de-944b-e07fc1f90ae7?user_id=a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

**Response -- 200**

```json
{
  "success": true,
  "data": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "deleted_at": "2026-03-19T16:00:00.000Z"
  }
}
```

---

## POST /api/v2/messages/:id/pin

Pin a message. Broadcasts pin event via WebSocket.

**Path Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | UUID of the message to pin. |

**Body Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `user_id` | string | No | UUID of the user pinning. Falls back to JWT user. |

**Example Request**

```bash
curl -X POST https://api.lvng.ai/api/v2/messages/7c9e6679-7425-40de-944b-e07fc1f90ae7/pin \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

**Response -- 200**

```json
{
  "success": true,
  "data": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Can you analyze the Q1 marketing data?",
    "metadata": {
      "pinned": true,
      "pinned_by": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
      "pinned_at": "2026-03-19T16:05:00.000Z"
    },
    "updated_at": "2026-03-19T16:05:00.000Z"
  }
}
```

---

## POST /api/v2/messages/:id/react

Toggle a reaction on a message. Adds if not present, removes if already exists.

**Path Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | UUID of the message to react to. |

**Body Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `user_id` | string | Yes | UUID of the user reacting. |
| `emoji` | string | Yes | Emoji string for the reaction (e.g. "thumbsup", "fire"). |

**Example Request**

```bash
curl -X POST https://api.lvng.ai/api/v2/messages/7c9e6679-7425-40de-944b-e07fc1f90ae7/react \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "emoji": "thumbsup"
  }'
```

**Response -- 200**

```json
{
  "success": true,
  "data": {
    "id": "d4735e3a-265e-16d0-8b4b-b7bfa8e5b9e0",
    "message_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "emoji": "thumbsup",
    "created_at": "2026-03-19T16:10:00.000Z"
  },
  "meta": {}
}
```

---

## POST /api/v2/messages/:id/thread

Reply in a thread. Creates a message with parent_message_id set to :id.

**Path Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | UUID of the parent message to reply to. |

**Body Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `content` | string | Yes | Reply content. |
| `content_type` | string | No | Content type identifier. |
| `user_id` | string | No | UUID of the replying user. Falls back to JWT user. |
| `ai_twin_id` | string | No | UUID of an AI twin sending this reply. |
| `metadata` | object | No | Arbitrary metadata for the reply. |

**Example Request**

```bash
curl -X POST https://api.lvng.ai/api/v2/messages/7c9e6679-7425-40de-944b-e07fc1f90ae7/thread \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Here is the Q1 analysis you requested.",
    "ai_twin_id": "b5b2c3d4-e5f6-7890-abcd-ef1234567890"
  }'
```

**Response -- 201**

```json
{
  "success": true,
  "data": {
    "id": "e3b0c442-98fc-1c14-b39f-27bd3e8cd9a1",
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "Here is the Q1 analysis you requested.",
    "content_type": "text",
    "user_id": null,
    "ai_twin_id": "b5b2c3d4-e5f6-7890-abcd-ef1234567890",
    "parent_message_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "metadata": {},
    "created_at": "2026-03-19T16:15:00.000Z",
    "updated_at": "2026-03-19T16:15:00.000Z"
  }
}
```

---

## POST /api/v2/messages/upload

Upload files via multipart form data. Stores in Supabase Storage.

**Body Parameters (multipart/form-data)**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `files` | File[] | Yes | One or more files (multipart form field). |
| `conversation_id` | string | Yes | UUID of the conversation to attach files to. |
| `workspace_id` | string | No | UUID of the workspace. |
| `conversation_type` | string | No | Type of conversation (e.g. channel, dm). |

**Example Request**

```bash
curl -X POST https://api.lvng.ai/api/v2/messages/upload \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -F "files=@report.pdf" \
  -F "files=@chart.png" \
  -F "conversation_id=550e8400-e29b-41d4-a716-446655440000"
```

**Response -- 200**

```json
{
  "success": true,
  "data": {
    "files": [
      {
        "name": "report.pdf",
        "url": "https://storage.lvng.ai/uploads/550e8400/report.pdf",
        "size": 245760,
        "type": "application/pdf"
      },
      {
        "name": "chart.png",
        "url": "https://storage.lvng.ai/uploads/550e8400/chart.png",
        "size": 89200,
        "type": "image/png"
      }
    ],
    "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
    "conversation_type": "channel"
  }
}
```

---

## POST /api/v2/messages/summarize

Generate an AI summary of channel messages using Claude Haiku.

**Body Parameters**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `channel_id` | string | Yes | UUID of the channel to summarize. |
| `messageCount` | integer | No | Number of recent messages to include. Defaults to all recent messages. |

**Example Request**

```bash
curl -X POST https://api.lvng.ai/api/v2/messages/summarize \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "messageCount": 50
  }'
```

**Response -- 200**

```json
{
  "success": true,
  "data": {
    "summary": "The team discussed the Q1 marketing report and deployment plans. Key decisions: marketing budget will increase 15% for Q2 targeting enterprise accounts, staging deployment scheduled for Friday with full integration tests, and Slack notifications will be added to the CI/CD pipeline.",
    "messageCount": 47,
    "timeRange": {
      "from": "2026-03-18T09:00:00.000Z",
      "to": "2026-03-19T15:12:45.000Z"
    }
  }
}
```
