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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.