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

# POST /api/threads

> Create a new conversation thread

## Authentication

**Required**: JWT Bearer token

**JWT Claims Extraction** (Lines 72-91):

```csharp theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sub | ClaimTypes.NameIdentifier → User UUID (required)
email | ClaimTypes.Email → User email
full_name | name | ClaimTypes.Name → Display name
```

## Request Body

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

  **Validation**: None (controller accepts null or any string, Line 94)

  **Default**: null allowed (service may generate default title)

  **Max Length**: Not enforced by API contract
</ParamField>

## Response

<ResponseField name="thread_id" type="string">
  UUID identifier for created thread
</ResponseField>

<ResponseField name="user_id" type="string">
  Owner UUID (matches authenticated user)
</ResponseField>

<ResponseField name="title" type="string">
  Thread title (as provided or service-generated)
</ResponseField>

<ResponseField name="visibility" type="string">
  Default visibility (implementation-defined, likely `"private"`)
</ResponseField>

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

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

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X POST 'http://localhost:5079/api/threads' \
    -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
    -H 'Content-Type: application/json' \
    -d '{"title": "New Conversation"}'
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  const response = await fetch('http://localhost:5079/api/threads', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${jwt}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      title: 'New Conversation'
    })
  });

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

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

  response = requests.post(
      'http://localhost:5079/api/threads',
      headers={
          'Authorization': f'Bearer {jwt}',
          'Content-Type': 'application/json'
      },
      json={'title': 'New Conversation'}
  )

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

<ResponseExample>
  ```json 200 Success theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "thread_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "user_id": "user-uuid-here",
    "title": "New Conversation",
    "visibility": "private",
    "created_at": "2024-01-15T10:30:00.000Z",
    "updated_at": "2024-01-15T10:30:00.000Z"
  }
  ```

  ```json 401 Unauthorized theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "error": "User ID claim missing"
  }
  ```

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

## Side Effects

**Database Mutations** (Lines 92-94):

1. **users** table UPSERT (Lines 83-92):
   ```csharp theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
   await _userSyncService.EnsureUserExistsAsync(userId.Value, email, name);
   ```
   * **Timing**: Happens **before** thread creation
   * **Purpose**: Ensures `users` table row exists (FK constraint requirement)
   * **Idempotent**: UPSERT operation
   * **Failure**: Would bubble to 500 error

2. **threads** table INSERT (Line 94):
   ```csharp theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
   await _threadsService.CreateThreadAsync(request?.Title, userId);
   ```
   * INSERT new thread row
   * Sets `user_id = userId` from JWT
   * Sets `visibility` to default value (service-defined)
   * Generates `thread_id` UUID
   * Sets `created_at` and `updated_at` timestamps

**No Cascade Effects**: Thread created empty (no messages yet)

## Permissions

**Who Can Create**:

* Any authenticated user

**Ownership**:

* Thread `user_id` set to authenticated user's UUID
* Cannot create threads for other users

## Validation

**Title Validation** (Line 94):

* null allowed
* Empty string allowed
* Whitespace-only allowed
* No length constraints enforced by controller

**User ID Validation** (Lines 78-81):

* MUST be valid GUID from JWT
* MUST exist in JWT claims
* Returns 401 if missing or invalid

## Authorization

**Creation Rights**:

* Authenticated user can create unlimited threads
* No quota enforcement by controller

**Initial Visibility**:

* Set by service (not specified in controller)
* Assumed default: `"private"`

## Edge Cases

1. **Null title**: Allowed, service may generate default (Line 94)
2. **Empty title**: Allowed
3. **Very long title**: Not validated by controller (database column limit may apply)
4. **Duplicate title**: Allowed (titles not unique)
5. **User doesn't exist in database**: Synced before creation (Lines 92)
6. **User sync fails**: 500 error (blocks thread creation)

## Error Conditions

| Code                  | HTTP | Cause                         | Controller Line |
| --------------------- | ---- | ----------------------------- | --------------- |
| N/A                   | 401  | JWT missing or invalid        | Middleware      |
| N/A                   | 401  | User ID claim missing/invalid | 78-81           |
| `THREAD_CREATE_ERROR` | 500  | User sync failure             | 98-106          |
| `THREAD_CREATE_ERROR` | 500  | Thread creation failure       | 98-106          |
| `THREAD_CREATE_ERROR` | 500  | Database constraint violation | 98-106          |

**Exception Handling** (Lines 98-106):

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

* All exceptions return 500
* Error message exposed to client

## Behavioral Guarantees

**Atomicity**: Not enforced by controller code

* User sync and thread creation not wrapped in transaction
* User sync failure blocks thread creation
* Thread creation failure leaves user synced (not rolled back)

**Idempotency**: NOT idempotent

* Each call creates new thread (even with same title)
* No deduplication logic

## Database Schema Dependencies

**Foreign Key**: `threads.user_id` → `users.id`

**Constraint Enforcement**: User must exist before thread creation (Line 92)
