> ## Documentation Index
> Fetch the complete documentation index at: https://dev-doc.dualmindlab.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# GET /api/threads/{id}/messages

> Retrieve all messages in a thread

## Authentication

**Conditional** (Line 198): `[AllowAnonymous]`

Follows same access rules as GET /api/threads/{id}

## Path Parameters

<ParamField path="id" type="string" required>
  Thread UUID

  **Format**: Valid GUID

  **Validation**: Route constraint `:guid` (Line 197)
</ParamField>

## Response

<ResponseField name="items" type="array">
  Array of message objects

  <Expandable title="Message object">
    <ResponseField name="message_id" type="string">
      UUID identifier
    </ResponseField>

    <ResponseField name="thread_id" type="string">
      Parent thread UUID
    </ResponseField>

    <ResponseField name="prompt_text" type="string">
      User's input message
    </ResponseField>

    <ResponseField name="model1_id" type="string">
      First model UUID (nullable for single-chat mode)
    </ResponseField>

    <ResponseField name="model2_id" type="string">
      Second model UUID (nullable for single-chat mode)
    </ResponseField>

    <ResponseField name="model1_response" type="string">
      First model's response text
    </ResponseField>

    <ResponseField name="model2_response" type="string">
      Second model's response text (nullable for single-chat)
    </ResponseField>

    <ResponseField name="comparison_id" type="string">
      Comparison UUID (only for dual-chat mode)
    </ResponseField>

    <ResponseField name="model1_time_ms" type="integer">
      First model inference time in milliseconds
    </ResponseField>

    <ResponseField name="model2_time_ms" type="integer">
      Second model inference time (nullable)
    </ResponseField>

    <ResponseField name="created_at" type="string">
      ISO8601 UTC timestamp
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X GET 'http://localhost:5079/api/threads/f47ac10b-58cc-4372-a567-0e02b2c3d479/messages' \
    -H 'Authorization: Bearer YOUR_JWT_TOKEN'
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  const response = await fetch(
    'http://localhost:5079/api/threads/f47ac10b-58cc-4372-a567-0e02b2c3d479/messages',
    {
      headers: { 'Authorization': `Bearer ${jwt}` }
    }
  );

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

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  import requests

  response = requests.get(
      'http://localhost:5079/api/threads/f47ac10b-58cc-4372-a567-0e02b2c3d479/messages',
      headers={'Authorization': f'Bearer {jwt}'}
  )

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

<ResponseExample>
  ```json 200 Success theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "items": [
      {
        "message_id": "msg-uuid-here",
        "thread_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "prompt_text": "What is AI?",
        "model1_id": "model-uuid-1",
        "model2_id": "model-uuid-2",
        "model1_response": "AI is artificial intelligence...",
        "model2_response": "Artificial intelligence refers to...",
        "comparison_id": "comparison-uuid",
        "model1_time_ms": 450,
        "model2_time_ms": 520,
        "created_at": "2024-01-15T10:30:00.000Z"
      }
    ]
  }
  ```

  ```json 404 NOT_FOUND theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "success": false,
    "error": "Thread not found",
    "code": "NOT_FOUND"
  }
  ```

  ```json 401 UNAUTHORIZED theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "success": false,
    "error": "Authentication required to access this thread",
    "code": "UNAUTHORIZED"
  }
  ```

  ```json 403 FORBIDDEN theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "success": false,
    "error": "You do not have access to this thread",
    "code": "FORBIDDEN"
  }
  ```

  ```json 500 MESSAGES_ERROR theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "success": false,
    "error": "Database query failed",
    "code": "MESSAGES_ERROR"
  }
  ```
</ResponseExample>

## Authorization Logic

**Identical to GET /api/threads/{id}** (Lines 203-249):

```csharp theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
// First check thread access (same logic as GetThread)
var thread = await _threadsService.GetThreadAsync(threadId);

// Apply same visibility + feature flag checks
if (!(publicSharingEnabled && (thread.Visibility == "public" || "unlisted"))) {
    // Require auth + ownership check
}
```

### Access Control Matrix

| Flag | Visibility | Auth        | Access    |
| ---- | ---------- | ----------- | --------- |
| ON   | public     | No          | ✅ Allowed |
| ON   | unlisted   | No          | ✅ Allowed |
| ON   | private    | No          | ❌ 401     |
| ON   | private    | Yes (owner) | ✅ Allowed |
| ON   | private    | Yes (other) | ❌ 403     |
| OFF  | \*         | No          | ❌ 401     |
| OFF  | \*         | Yes (owner) | ✅ Allowed |
| OFF  | \*         | Yes (other) | ❌ 403     |

## Side Effects

**Database Reads** (Lines 204, 216, 251):

* SELECT from `threads` table WHERE `thread_id = {id}`
* SELECT from `system_settings` table WHERE `key = 'public_sharing'`
* SELECT from `thread_messages` table WHERE `thread_id = {id}`

**No Database Writes**: Read-only endpoint

**Message Ordering**: Not specified by API contract (service-defined, likely chronological)

## Permissions

**Same as Thread Access**:

* If user can read thread, user can read messages
* No separate message-level permissions

**Cascade Rule**: Message visibility = Thread visibility

## Edge Cases

1. **Thread doesn't exist**: 404 (Lines 206-214)
2. **Thread exists but has zero messages**: Returns `{"items": []}` (Line 253)
3. **Invalid GUID format**: 400 (route constraint)
4. **Deleted thread**: 404 (service returns null for thread)
5. **Orphaned messages** (thread deleted): Cannot occur (thread lookup first)
6. **Partial message data** (model2 null in single-chat): Nullable fields return null

## Error Conditions

| Code             | HTTP | Cause                      | Controller Line |
| ---------------- | ---- | -------------------------- | --------------- |
| `NOT_FOUND`      | 404  | Thread doesn't exist       | 206-214         |
| `UNAUTHORIZED`   | 401  | Auth required but missing  | 230-238         |
| `FORBIDDEN`      | 403  | Private thread, wrong user | 240-248         |
| `MESSAGES_ERROR` | 500  | Service exception          | 255-263         |

**Exception Handling** (Lines 255-263):

```csharp theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
catch (Exception ex) {
    return StatusCode(500, new { error = ex.Message, code = "MESSAGES_ERROR" });
}
```

## Behavioral Guarantees

**Two-Step Verification** (Lines 204-249):

1. Thread existence + access check
2. Messages retrieval

**Atomicity**: Not transactional

* Thread could be deleted between checks (race condition)
* Service behavior on race condition not enforced by server contract

**Pagination**: Not supported

* Returns ALL messages for thread
* No limit parameter
* Could cause performance issues for large threads

## Performance Characteristics

**Database Queries**: 3 queries per request

1. Thread lookup
2. Feature flag lookup
3. Messages retrieval

**Response Size**: Unbounded (proportional to message count)

**Index Requirements**:

* `thread_messages.thread_id` should be indexed
* `threads.thread_id` primary key

**Performance Risk**: Large threads (100+ messages) may cause slow responses

## Message Schema Details

**Single-Chat Mode**:

* `model1_id`: Populated
* `model2_id`: NULL
* `model1_response`: Populated
* `model2_response`: NULL
* `comparison_id`: NULL

**Dual-Chat Mode**:

* `model1_id`: Populated
* `model2_id`: Populated
* `model1_response`: Populated
* `model2_response`: Populated
* `comparison_id`: Populated (links to `comparisons` table)

**Comparison ID Usage**:

* Can be used with GET /api/arena/model-stats to see vote data
* Links message to arena comparison for voting
