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

# Chat

> SSE chat stream, thread CRUD, and credit reservation.

The chat endpoint returns a **Server-Sent Events (SSE) stream**, not JSON. Each response chunk contains a text-delta that the client aggregates into the full assistant message.

## Endpoints

| Method | Path                                         | Scope        | Description                       |
| ------ | -------------------------------------------- | ------------ | --------------------------------- |
| POST   | `/api/v1/chat`                               | `chat:write` | Stream an assistant turn (SSE)    |
| GET    | `/api/v1/threads?workspace={workspace}`      | `chat:read`  | List chat threads                 |
| POST   | `/api/v1/threads`                            | `chat:write` | Create a chat thread              |
| GET    | `/api/v1/threads/{id}?workspace={workspace}` | `chat:read`  | Get a thread with its messages    |
| PATCH  | `/api/v1/threads/{id}`                       | `chat:write` | Update thread title or pin status |
| DELETE | `/api/v1/threads/{id}?workspace={workspace}` | `chat:write` | Delete a thread                   |

## Stream a chat turn

```bash theme={null}
curl -X POST https://ai-cmo.dev/api/v1/chat \
  -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
"workspace": "{workspace}",
    "threadId": "{threadId}",
    "messages": [
      {
        "id": "msg_001",
        "role": "user",
        "parts": [{ "type": "text", "text": "What is my current GEO score?" }]
      }
    ]
  }'
```

The response is `Content-Type: text/event-stream`. Each event contains a text-delta that must be aggregated client-side:

```text theme={null}
event: text-delta
data: {"type":"text-delta","text":"Your","index":0}

event: text-delta
data: {"type":"text-delta","text":" GEO","index":0}

event: text-delta
data: {"type":"text-delta","text":" score","index":0}

event: finish
data: {"type":"finish","finishReason":"stop","usage":{"promptTokens":120,"completionTokens":85}}
```

### Required fields

<ParamField name="workspace" type="string" required>
  Workspace slug.
</ParamField>

<ParamField name="threadId" type="string" required>
  Existing thread ID (create one first).
</ParamField>

<ParamField name="messages" type="array" required>
  Message objects with `id`, `role`, `parts`.
</ParamField>

### Idempotency

The server accepts an optional `Idempotency-Key` header. If absent, the server generates one automatically.

## Thread CRUD

### List threads

```bash theme={null}
curl -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
     "https://ai-cmo.dev/api/v1/threads?workspace={workspace}"
```

### Create a thread

```bash theme={null}
curl -X POST https://ai-cmo.dev/api/v1/threads \
  -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"workspace": "{workspace}", "title": "GEO analysis"}'
```

If omitted, `title` defaults to "New conversation".

### Get a thread

```bash theme={null}
curl -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://ai-cmo.dev/api/v1/threads/{id}?workspace={workspace}"
```

Returns the thread metadata and all messages.

### Update a thread

```bash theme={null}
curl -X PATCH https://ai-cmo.dev/api/v1/threads/{id} \
  -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"workspace": "{workspace}", "title": "Updated title", "pinned": true}'
```

### Delete a thread

```bash theme={null}
curl -X DELETE "https://ai-cmo.dev/api/v1/threads/{id}?workspace={workspace}" \
  -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

## Credit reservation

Chat uses 720,000 µUSD per turn (6 steps at 120,000 µUSD per step). Each step within a turn obtains a per-step credit lease. If a lease is refused mid-turn, the stream terminates with `stopped_credits`.

## Consuming the stream

The recommended approach (used by the MCP client at `apps/mcp/src/client.ts`) is to aggregate text-delta events into a full message:

<Steps>
  <Step title="Connect the stream">
    Connect the SSE stream to a POST to `/api/v1/chat`.
  </Step>

  <Step title="Aggregate the deltas">
    For each `text-delta` event, append `text` to the current assistant message.
  </Step>

  <Step title="Detect completion">
    On `finish`, the stream is complete.
  </Step>

  <Step title="Persist the history">
    After the stream, use `PATCH /api/v1/threads/{id}` or the internal `saveChatMessages` to persist the full message history.
  </Step>
</Steps>
