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

# Thesis API

> Create, evolve, evaluate, and publish theses over HTTP. The full thesis-lifecycle surface — create, signal, evaluate, augment, fork, node mutation, positions, strategies, publishing, video.

Thesis owner endpoints live under `/api/thesis/*` and require authentication (`Authorization: Bearer sf_live_...` or browser session), except the public ticker lookup noted below. The CLI, TypeScript SDK, and Agent SDK call the same routes — see [Thesis lifecycle](/build/thesis-lifecycle) for the conceptual model and [Heartbeat](/concepts/heartbeat) for the per-thesis monitor loop.

A private thesis is owned by exactly one user. Owner read and write paths 404 when the caller does not own the resource. Public thesis reads return only published thesis data.

## CRUD

| Method   | Path                             | Purpose                                                                   |
| -------- | -------------------------------- | ------------------------------------------------------------------------- |
| `GET`    | `/api/thesis`                    | List your theses.                                                         |
| `POST`   | `/api/thesis/create`             | Create a new thesis. Optional `?sync=true` waits for formation.           |
| `GET`    | `/api/thesis/{id}`               | Full detail — tree, metadata, positions, strategies.                      |
| `PATCH`  | `/api/thesis/{id}`               | Update `title`, `webhookUrl`, `status`, or `metadata`.                    |
| `DELETE` | `/api/thesis/{id}`               | Delete the thesis and all related rows.                                   |
| `GET`    | `/api/thesis/by-ticker/{ticker}` | Public lookup for a published thesis referencing a market ticker, if any. |

### POST /api/thesis/create

Two modes:

* `POST /api/thesis/create` — returns `202` immediately and forms the causal tree in the background.
* `POST /api/thesis/create?sync=true` — runs formation inline (up to 5 minutes) and returns the formed thesis on `200`.

**Body**

| Field        | Type           | Required | Notes                                                                                                                              |
| ------------ | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `rawThesis`  | string         | yes      | Testable claim, ≥ 20 characters. Examples: `"Bitcoin closes 2026 above $50,000"`, `"The Fed cuts rates at least once in Q2 2026"`. |
| `title`      | string         | optional | Display title. Defaults to a derived short form of `rawThesis`.                                                                    |
| `webhookUrl` | string (https) | optional | Per-thesis webhook called when evaluations / status changes happen.                                                                |
| `metadata`   | object         | optional | Free-form metadata stored on the thesis. Heartbeat writes its config under `metadata.heartbeat`.                                   |

A claim classifier rejects rationale, action verbs, single tickers, or unverifiable statements with `code: THESIS_NOT_A_CLAIM`. Statements shorter than 20 chars return `THESIS_TOO_SHORT`.

**Response (202 / 200)**

```json theme={null}
{
  "id": "thesis_01J0...",
  "status": "forming",
  "title": "Fed cuts in 2026",
  "tree": null,
  "createdAt": "2026-05-05T20:01:33.000Z"
}
```

With `?sync=true`, `tree` is populated and `status` is `active` on success.

**Errors**

| Status | `code` / `error`        | Cause                                |
| ------ | ----------------------- | ------------------------------------ |
| `400`  | `rawThesis is required` | Missing field.                       |
| `400`  | `THESIS_TOO_SHORT`      | \< 20 chars after trim.              |
| `400`  | `THESIS_NOT_A_CLAIM`    | Classifier rejected as non-testable. |
| `401`  | unauthorized            | No / invalid auth.                   |

### GET /api/thesis/{id}

Returns the full state, including tree, positions, and strategies merged into one document.

```json theme={null}
{
  "id": "thesis_01J0...",
  "title": "Fed cuts in 2026",
  "rawThesis": "...",
  "status": "active",
  "tree": { /* root node, children, edges */ },
  "metadata": { "heartbeat": { /* see /concepts/heartbeat */ } },
  "positions": [ /* see Positions section */ ],
  "strategies": [ /* see Strategies section */ ],
  "createdAt": "...",
  "updatedAt": "..."
}
```

### PATCH /api/thesis/{id}

**Body** (all optional)

| Field        | Type                                  | Notes                                                                                    |
| ------------ | ------------------------------------- | ---------------------------------------------------------------------------------------- |
| `title`      | string                                | Display title.                                                                           |
| `webhookUrl` | string \| null                        | Set or clear the per-thesis webhook.                                                     |
| `status`     | `"active" \| "archived" \| "watched"` | Lifecycle.                                                                               |
| `metadata`   | object                                | Replaces metadata at the top level. To preserve heartbeat config, read first then merge. |

Returns the updated thesis. `404` when the thesis is not yours.

### DELETE /api/thesis/{id}

Hard-deletes the thesis and all related rows (signals, evaluations, positions, strategies, notifications). Returns `{ ok: true }` on success.

## Signals

```http theme={null}
POST /api/thesis/{id}/signal
```

Inject evidence into a thesis. The monitor loop also writes signals automatically; this endpoint is for analyst notes, off-platform news, or programmatic forwards.

**Body**

| Field     | Type   | Required | Notes                                                 |
| --------- | ------ | -------- | ----------------------------------------------------- |
| `type`    | string | yes      | One of `news`, `price_move`, `user_note`, `external`. |
| `content` | string | yes      | Free-form content — headline, observation, link.      |
| `source`  | string | optional | Display source attribution (URL or name).             |

**Response 200**

```json theme={null}
{ "id": "sig_...", "thesisId": "thesis_...", "type": "news", "createdAt": "..." }
```

**Errors**: `400 type and content are required`, `404 Thesis not found`.

## Evaluation

```http theme={null}
POST /api/thesis/{id}/evaluate
```

Triggers a deep evaluation cycle on demand. Heartbeat runs evaluations on its own cadence — only call this for an immediate refresh.

**Errors**

| Status | Body                             | Cause                                           |
| ------ | -------------------------------- | ----------------------------------------------- |
| `400`  | `Thesis is <status>, not active` | Cannot evaluate `archived` or `forming` theses. |
| `404`  | `Thesis not found`               | Wrong owner / id.                               |

**Response 200**: full evaluation result — confidence, node probabilities, signals consumed, killConditions checked, model used, cost.

## Augment

```http theme={null}
POST /api/thesis/{id}/augment
```

Runs LLM-driven causal-tree augmentation. The model proposes new nodes, edges, and probabilities based on the current state and recent signals.

**Body:** none required. Optionally pass `{ depth?: number, focusNodeId?: string }` to scope the augmentation.

**Response 200**

```json theme={null}
{
  "thesisId": "thesis_01J0...",
  "addedNodes": 3,
  "updatedNodes": 5,
  "tree": { /* full updated causal tree */ },
  "modelUsed": "anthropic/claude-sonnet-4.6",
  "costUsd": 0.0042
}
```

**Errors**

| Status | Body                             | Cause                                     |
| ------ | -------------------------------- | ----------------------------------------- |
| `400`  | `Thesis is <status>, not active` | Cannot augment unless `status: "active"`. |
| `404`  | `Thesis not found`               | Wrong owner / id.                         |

## Causal-tree node mutation

```http theme={null}
POST /api/thesis/{id}/nodes
```

Direct, zero-LLM-cost edits to the tree. Use this when an analyst overrides a probability or locks a node from automatic updates.

**Body**

| Field     | Type             | Required | Notes                                                                             |
| --------- | ---------------- | -------- | --------------------------------------------------------------------------------- |
| `updates` | array            | yes      | Each entry: `{ nodeId: string, probability: number /* 0-1 */, reason?: string }`. |
| `lock`    | array of nodeIds | optional | Lock these nodes from automatic updates until manually unlocked.                  |

**Errors**

| Status | Body                                       | Cause                 |
| ------ | ------------------------------------------ | --------------------- |
| `400`  | `updates array required`                   | Missing or empty.     |
| `400`  | `Each update needs nodeId and probability` | Malformed entry.      |
| `400`  | `probability must be 0-1, got X for nodeY` | Out of range.         |
| `400`  | `Thesis has no causal tree`                | Thesis still forming. |
| `404`  | `Node X not found in causal tree`          | Bad nodeId.           |

## Fork / evolve

```http theme={null}
POST /api/thesis/{id}/fork
```

Two modes, both POST:

* **Pure fork**: empty body or `{}` — clones the thesis as-is for a new owner-perspective.
* **Evolve**: send `{ newRawThesis, newTitle?, reason?, inheritEdgeMarketIds? }` — creates a new thesis as a frame-shift of the old one. Owner-only.

**Errors**

| Status | Body                                                  | Cause                     |
| ------ | ----------------------------------------------------- | ------------------------- |
| `403`  | `Only the owner can evolve a thesis into a new frame` | Evolve mode by non-owner. |

## Context / read

| Method | Path                                 | Purpose                                                      |
| ------ | ------------------------------------ | ------------------------------------------------------------ |
| `GET`  | `/api/thesis/{id}/context`           | Tree + edges + positions in agent-shaped format.             |
| `GET`  | `/api/thesis/{id}/changes?since=...` | Changes since timestamp.                                     |
| `GET`  | `/api/thesis/{id}/prompt`            | Generated agent prompt for this thesis.                      |
| `GET`  | `/api/thesis/{id}/evaluations`       | Evaluation history (daily-aggregated confidence trajectory). |

## Heartbeat

```http theme={null}
GET   /api/thesis/{id}/heartbeat
PATCH /api/thesis/{id}/heartbeat
```

See [Heartbeat](/concepts/heartbeat) for the full schema, validation ranges, defaults, and CLI mapping.

## Positions

```http theme={null}
GET    /api/thesis/{id}/positions
POST   /api/thesis/{id}/positions
PATCH  /api/thesis/{id}/positions/{posId}
DELETE /api/thesis/{id}/positions/{posId}
```

Positions linked to a thesis. The runtime, autopilot, and CLI write to this surface; user-side code can also attach external positions for backtests or analyst notes.

### POST body — required

| Field              | Type                         | Notes                                    |
| ------------------ | ---------------------------- | ---------------------------------------- |
| `venue`            | `"kalshi"` \| `"polymarket"` | Venue.                                   |
| `externalMarketId` | string                       | Kalshi ticker or Polymarket conditionId. |
| `marketTitle`      | string                       | Display title saved on the position.     |
| `direction`        | `"yes"` \| `"no"`            | Contract side.                           |
| `entryPrice`       | number                       | Entry price in cents.                    |

### POST body — optional

| Field      | Type    | Notes              |
| ---------- | ------- | ------------------ |
| `quantity` | integer | Contract quantity. |
| `notes`    | string  | Analyst notes.     |
| `metadata` | object  | Free-form.         |

Returns `{ id: <posId> }` with status `201`.

PATCH accepts any subset of those fields. DELETE records an exit timestamp; positions are not hard-deleted.

## Strategies

```http theme={null}
GET    /api/thesis/{id}/strategies        ?status=active
POST   /api/thesis/{id}/strategies
PATCH  /api/thesis/{id}/strategies/{sid}
DELETE /api/thesis/{id}/strategies/{sid}
```

A strategy is a structured plan for trading the thesis — direction, horizon, entry / stop / take-profit, sizing, soft conditions. Both `POST` and `PATCH` accept the same body shape; `POST` requires the core fields, `PATCH` only the fields you want to change.

### Body fields

| Field              | Type                                     | Notes                                           |
| ------------------ | ---------------------------------------- | ----------------------------------------------- |
| `direction`        | `"long"` \| `"short"`                    | Strategy direction.                             |
| `horizon`          | string                                   | Time horizon (`"1d"`, `"1w"`, `"1m"`, ...).     |
| `entryBelow`       | number                                   | Enter long below this price (cents).            |
| `entryAbove`       | number                                   | Enter short above this price (cents).           |
| `stopLoss`         | number                                   | Stop in cents.                                  |
| `takeProfit`       | number                                   | Take-profit in cents.                           |
| `maxQuantity`      | integer                                  | Max contracts across the strategy.              |
| `perOrderQuantity` | integer                                  | Max contracts per order.                        |
| `softConditions`   | string\[]                                | NL conditions evaluated by smart-mode runtime.  |
| `rationale`        | string                                   | Why-string.                                     |
| `entry`            | object                                   | Structured entry plan.                          |
| `exit`             | object                                   | Structured exit plan.                           |
| `sizing`           | object                                   | Sizing plan (Kelly, fixed, ...).                |
| `priority`         | integer                                  | Higher priority strategies are evaluated first. |
| `status`           | `"active"` \| `"paused"` \| `"archived"` | Lifecycle.                                      |
| `executedQuantity` | integer                                  | Read-mostly; tracked by the runtime.            |

## Publishing

```http theme={null}
POST   /api/thesis/{id}/publish
DELETE /api/thesis/{id}/publish
```

### POST body

| Field         | Type   | Required | Notes                                       |
| ------------- | ------ | -------- | ------------------------------------------- |
| `slug`        | string | yes      | URL slug (lowercase, hyphens, 3–60 chars).  |
| `description` | string | optional | Short description shown on the public page. |

**Response 200**

```json theme={null}
{ "published": true, "url": "/thesis/my-fed-cut-thesis" }
```

**Errors**

| Status | Body               | Cause                                                                      |
| ------ | ------------------ | -------------------------------------------------------------------------- |
| `400`  | `slug is required` | Missing slug.                                                              |
| `400`  | `<message>`        | Slug already in use, slug malformed, or thesis not in a publishable state. |
| `401`  | unauthorized       | No / invalid auth.                                                         |

### DELETE

Returns `{ "unpublished": true }`. The public page returns 404 immediately; the underlying thesis is unaffected.

Public theses are reachable at `/thesis/{slug}` for browsers and `GET /api/public/thesis/{slug}` for agents.

## Public thesis reads

| Method | Path                             | Purpose                                                                                              |
| ------ | -------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `GET`  | `/api/public/theses`             | List published theses. Optional `?changes_only=true` filters to theses with material recent changes. |
| `GET`  | `/api/public/thesis/{slug}`      | Public thesis detail by slug.                                                                        |
| `GET`  | `/api/thesis/by-ticker/{ticker}` | Public ticker-to-thesis lookup for published theses.                                                 |

These public endpoints do not require browser session ownership. SDK and Agent SDK callers still use an API-keyed client by default because the strict SDK/Agent contract is API-key-first for hosted reads.

## What-if

```http theme={null}
POST /api/thesis/{id}/whatif
```

Runs a counter-factual evaluation. The response shows projected confidence and edge metrics under the override scenario without writing an evaluation row.

### Body

| Field       | Type   | Notes                                                                                                                                                            |
| ----------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `overrides` | object | `{ <nodeId>: <probability 0-1>, ... }` — values to apply on top of the current causal tree. Empty object is allowed; the result is the current state recomputed. |

**Errors**

| Status | Body                                          | Cause                     |
| ------ | --------------------------------------------- | ------------------------- |
| `400`  | `Thesis missing causal tree or edge analysis` | Thesis hasn't formed yet. |
| `404`  | `Thesis not found`                            | Wrong owner / id.         |

## Video

| Method | Path                          | Notes                                          |
| ------ | ----------------------------- | ---------------------------------------------- |
| `GET`  | `/api/thesis/{id}/videos`     | List linked videos.                            |
| `POST` | `/api/thesis/{id}/videos`     | Attach a video record.                         |
| `GET`  | `/api/thesis/{id}/video-data` | Render-ready data bundle for video generation. |

## SDK and Agent SDK mapping

The TypeScript SDK exposes this thesis surface under `sf.theses.*`:

| SDK method                                          | Route                                       |         |
| --------------------------------------------------- | ------------------------------------------- | ------- |
| `sf.theses.list()`                                  | `GET /api/thesis`                           |         |
| `sf.theses.get(id)`                                 | `GET /api/thesis/{id}`                      |         |
| `sf.theses.update(id, body)`                        | `PATCH /api/thesis/{id}`                    |         |
| `sf.theses.delete(id)`                              | `DELETE /api/thesis/{id}`                   |         |
| `sf.theses.create(body)`                            | `POST /api/thesis/create`                   |         |
| `sf.theses.signal(id, body)`                        | `POST /api/thesis/{id}/signal`              |         |
| `sf.theses.context(id)`                             | `GET /api/thesis/{id}/context`              |         |
| `sf.theses.changes(id, { since })`                  | `GET /api/thesis/{id}/changes`              |         |
| `sf.theses.heartbeat.get(id)` / `.update(id, body)` | `GET` / `PATCH /api/thesis/{id}/heartbeat`  |         |
| `sf.theses.nodes.update(id, body)`                  | `POST /api/thesis/{id}/nodes`               |         |
| `sf.theses.positions.*`                             | `/api/thesis/{id}/positions`                |         |
| `sf.theses.strategies.*`                            | `/api/thesis/{id}/strategies`               |         |
| `sf.theses.evaluations.list(id)`                    | `GET /api/thesis/{id}/evaluations`          |         |
| `sf.theses.whatIf(id, body)`                        | `POST /api/thesis/{id}/whatif`              |         |
| `sf.theses.evaluate(id)`                            | `POST /api/thesis/{id}/evaluate`            |         |
| `sf.theses.augment(id, { dryRun? })`                | \`POST /api/thesis/{id}/augment?dryRun=true | false\` |
| `sf.theses.fork(id, body)`                          | `POST /api/thesis/{id}/fork`                |         |
| `sf.theses.publish(id, body)` / `.unpublish(id)`    | `POST` / `DELETE /api/thesis/{id}/publish`  |         |
| `sf.theses.publicList()`                            | `GET /api/public/theses`                    |         |
| `sf.theses.publicGet(slug)`                         | `GET /api/public/thesis/{slug}`             |         |
| `sf.theses.publicByTicker(ticker)`                  | `GET /api/thesis/by-ticker/{ticker}`        |         |

Agent SDK canonical tools mirror the same names with dotted tool ids, for example `theses.context`, `theses.heartbeat.update`, `theses.positions.create`, `theses.strategies.list`, `theses.evaluate`, and `theses.public.get`. Read tools have `sideEffect: none`; thesis mutation, evaluation, augmentation, fork, publish, and unpublish tools are `sideEffect: user_write` and require explicit Agent policy allowance.

## CLI shorthand

| CLI                          | Endpoint                                   |
| ---------------------------- | ------------------------------------------ |
| `sf list`                    | `GET /api/thesis`                          |
| `sf get <id>`                | `GET /api/thesis/{id}`                     |
| `sf create "<thesis text>"`  | `POST /api/thesis/create`                  |
| `sf signal <id> "<content>"` | `POST /api/thesis/{id}/signal`             |
| `sf evaluate <id>`           | `POST /api/thesis/{id}/evaluate`           |
| `sf augment <id>`            | `POST /api/thesis/{id}/augment`            |
| `sf heartbeat <id>`          | `GET` / `PATCH /api/thesis/{id}/heartbeat` |
| `sf publish <id> --slug X`   | `POST /api/thesis/{id}/publish`            |
| `sf unpublish <id>`          | `DELETE /api/thesis/{id}/publish`          |
| `sf whatif <id>`             | `POST /api/thesis/{id}/whatif`             |
| `sf delta <id>`              | `GET /api/thesis/{id}/changes`             |

## See also

<CardGroup cols={2}>
  <Card title="Thesis lifecycle" href="/build/thesis-lifecycle">
    Conceptual model and CLI walkthrough.
  </Card>

  <Card title="Heartbeat" href="/concepts/heartbeat">
    Per-thesis monitor loop schema.
  </Card>

  <Card title="Direct API access" href="/build/direct-api-access">
    Auth, base URLs, and first calls.
  </Card>

  <Card title="Errors" href="/reference/errors">
    Full error envelope reference.
  </Card>
</CardGroup>
