> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/ghuntley/loom/llms.txt
> Use this file to discover all available pages before exploring further.

# Thread Endpoints

> Manage conversation threads and search thread history

## Overview

Threads represent conversation sessions between users and the Loom AI agent. Each thread contains a sequence of messages, tool calls, and execution state.

**Ownership**: Users can only access threads they own. System administrators can access all threads.

## Create or Update Thread

```http theme={null}
PUT /api/threads/{id}
```

Creates a new thread or updates an existing one. Supports optimistic concurrency control via the `If-Match` header.

### Path Parameters

<ParamField path="id" type="string" required>
  Thread ID (must match ID in request body)
</ParamField>

### Headers

<ParamField header="If-Match" type="integer" optional>
  Expected thread version for optimistic locking
</ParamField>

### Request Body

Send the full `Thread` object (see `loom-common-thread` schema):

<ParamField body="id" type="string" required>
  Thread ID (must match path parameter)
</ParamField>

<ParamField body="title" type="string" optional>
  Thread title
</ParamField>

<ParamField body="workspace" type="string" optional>
  Workspace root path
</ParamField>

<ParamField body="visibility" type="string" required>
  Visibility level: `"private"`, `"shared"`, or `"public"`
</ParamField>

<ParamField body="messages" type="array" required>
  Array of message objects
</ParamField>

<ParamField body="version" type="integer" required>
  Thread version (increment on each update)
</ParamField>

<ParamField body="updated_at" type="string" required>
  ISO 8601 timestamp (server will overwrite)
</ParamField>

### Response

<ResponseField name="id" type="string">
  Thread ID
</ResponseField>

<ResponseField name="version" type="integer">
  New version number after upsert
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO 8601 timestamp of last update
</ResponseField>

### Example

```bash theme={null}
curl -X PUT https://loom.ghuntley.com/api/threads/thread_abc123 \
  -H "Authorization: Bearer loom_sk_..." \
  -H "Content-Type: application/json" \
  -H "If-Match: 5" \
  -d '{
    "id": "thread_abc123",
    "title": "Fix login bug",
    "workspace": "/home/user/project",
    "visibility": "private",
    "messages": [...],
    "version": 5,
    "updated_at": "2026-03-03T12:00:00Z"
  }'
```

**Response (200 OK)**:

```json theme={null}
{
  "id": "thread_abc123",
  "title": "Fix login bug",
  "workspace": "/home/user/project",
  "visibility": "private",
  "messages": [...],
  "version": 6,
  "updated_at": "2026-03-03T12:05:30Z"
}
```

**Error (409 Conflict)** if version mismatch:

```json theme={null}
{
  "error": "version_conflict",
  "message": "Expected version 5, but current version is 7"
}
```

## Get Thread

```http theme={null}
GET /api/threads/{id}
```

Retrieves a single thread by ID. Only the thread owner (or system admin) can access.

### Path Parameters

<ParamField path="id" type="string" required>
  Thread ID
</ParamField>

### Response

Returns the full `Thread` object.

### Example

```bash theme={null}
curl -H "Authorization: Bearer loom_sk_..." \
  https://loom.ghuntley.com/api/threads/thread_abc123
```

```json theme={null}
{
  "id": "thread_abc123",
  "title": "Fix login bug",
  "workspace": "/home/user/project",
  "visibility": "private",
  "messages": [
    {
      "role": "user",
      "content": "Help me fix the login bug",
      "timestamp": "2026-03-03T12:00:00Z"
    },
    {
      "role": "assistant",
      "content": "I'll help you investigate the login bug.",
      "timestamp": "2026-03-03T12:00:15Z"
    }
  ],
  "version": 6,
  "updated_at": "2026-03-03T12:05:30Z",
  "created_at": "2026-03-01T10:00:00Z"
}
```

**Error (404 Not Found)** if thread doesn't exist or user doesn't own it:

```json theme={null}
{
  "error": "not_found",
  "message": "Thread not found"
}
```

## List Threads

```http theme={null}
GET /api/threads
```

Lists threads owned by the authenticated user. Supports filtering by workspace and pagination.

### Query Parameters

<ParamField query="workspace" type="string" optional>
  Filter by workspace root path
</ParamField>

<ParamField query="limit" type="integer" default="50">
  Maximum number of results (1-100)
</ParamField>

<ParamField query="offset" type="integer" default="0">
  Number of results to skip for pagination
</ParamField>

### Response

<ResponseField name="threads" type="array">
  Array of `ThreadSummary` objects (lightweight version without full message history)
</ResponseField>

<ResponseField name="total" type="integer">
  Total number of threads matching the filter
</ResponseField>

<ResponseField name="limit" type="integer">
  Limit from request
</ResponseField>

<ResponseField name="offset" type="integer">
  Offset from request
</ResponseField>

### Example

```bash theme={null}
curl "https://loom.ghuntley.com/api/threads?workspace=/home/user/project&limit=20&offset=0" \
  -H "Authorization: Bearer loom_sk_..."
```

```json theme={null}
{
  "threads": [
    {
      "id": "thread_abc123",
      "title": "Fix login bug",
      "workspace": "/home/user/project",
      "visibility": "private",
      "message_count": 12,
      "updated_at": "2026-03-03T12:05:30Z",
      "created_at": "2026-03-01T10:00:00Z"
    },
    {
      "id": "thread_def456",
      "title": "Add user settings page",
      "workspace": "/home/user/project",
      "visibility": "private",
      "message_count": 8,
      "updated_at": "2026-03-02T15:30:00Z",
      "created_at": "2026-03-02T14:00:00Z"
    }
  ],
  "total": 47,
  "limit": 20,
  "offset": 0
}
```

## Delete Thread

```http theme={null}
DELETE /api/threads/{id}
```

Soft-deletes a thread. Only the thread owner (or system admin) can delete.

### Path Parameters

<ParamField path="id" type="string" required>
  Thread ID
</ParamField>

### Response

Returns `204 No Content` on success.

### Example

```bash theme={null}
curl -X DELETE https://loom.ghuntley.com/api/threads/thread_abc123 \
  -H "Authorization: Bearer loom_sk_..."
```

**Response**: `204 No Content`

**Error (404 Not Found)** if thread doesn't exist or user doesn't own it.

## Update Thread Visibility

```http theme={null}
POST /api/threads/{id}/visibility
```

Updates only the thread visibility without syncing the full thread content. Supports optimistic locking.

### Path Parameters

<ParamField path="id" type="string" required>
  Thread ID
</ParamField>

### Headers

<ParamField header="If-Match" type="integer" optional>
  Expected thread version
</ParamField>

### Request Body

<ParamField body="visibility" type="string" required>
  New visibility: `"private"`, `"shared"`, or `"public"`
</ParamField>

### Response

Returns the updated `Thread` object with incremented version.

### Example

```bash theme={null}
curl -X POST https://loom.ghuntley.com/api/threads/thread_abc123/visibility \
  -H "Authorization: Bearer loom_sk_..." \
  -H "If-Match: 6" \
  -H "Content-Type: application/json" \
  -d '{"visibility": "shared"}'
```

```json theme={null}
{
  "id": "thread_abc123",
  "title": "Fix login bug",
  "visibility": "shared",
  "version": 7,
  "updated_at": "2026-03-03T13:00:00Z",
  ...
}
```

## Search Threads

```http theme={null}
GET /api/threads/search
```

Full-text search across thread titles and messages. Searches only threads owned by the authenticated user.

### Query Parameters

<ParamField query="q" type="string" required>
  Search query
</ParamField>

<ParamField query="workspace" type="string" optional>
  Filter by workspace root
</ParamField>

<ParamField query="limit" type="integer" default="50">
  Maximum number of results
</ParamField>

<ParamField query="offset" type="integer" default="0">
  Pagination offset
</ParamField>

### Response

<ResponseField name="hits" type="array">
  Array of search results with relevance scores
</ResponseField>

<ResponseField name="hits[].summary" type="object">
  ThreadSummary object
</ResponseField>

<ResponseField name="hits[].score" type="number">
  Relevance score (higher = more relevant)
</ResponseField>

<ResponseField name="limit" type="integer">
  Limit from request
</ResponseField>

<ResponseField name="offset" type="integer">
  Offset from request
</ResponseField>

### Example

```bash theme={null}
curl "https://loom.ghuntley.com/api/threads/search?q=login+bug&limit=10" \
  -H "Authorization: Bearer loom_sk_..."
```

```json theme={null}
{
  "hits": [
    {
      "summary": {
        "id": "thread_abc123",
        "title": "Fix login bug",
        "workspace": "/home/user/project",
        "message_count": 12,
        "updated_at": "2026-03-03T12:05:30Z"
      },
      "score": 0.92
    },
    {
      "summary": {
        "id": "thread_ghi789",
        "title": "Login form validation",
        "workspace": "/home/user/project",
        "message_count": 5,
        "updated_at": "2026-02-28T09:00:00Z"
      },
      "score": 0.67
    }
  ],
  "limit": 10,
  "offset": 0
}
```

**Error (400 Bad Request)** if search query is empty:

```json theme={null}
{
  "error": "bad_request",
  "message": "Empty search query"
}
```
