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

# Heartbeat

> Per-thesis monitoring loop — news scan cadence, social cadence, evaluation model tier, monthly budget, and closed-loop intent behavior.

Heartbeat is the per-thesis monitor loop. It controls how often SimpleFunctions scans news and social context for a thesis, which model tier evaluates new evidence, the monthly cost ceiling, and whether the monitor is allowed to create closed-loop entry or exit intents.

Heartbeat work is independent of thesis lifecycle status. A thesis can be `active` while its heartbeat is `paused`, and pausing the heartbeat never archives or closes the thesis.

## CLI

```bash theme={null}
# show config + this-month cost summary
sf heartbeat <thesis-id>
sf heartbeat <thesis-id> --json

# update fields (any combination)
sf heartbeat <thesis-id> --news-interval 60      # minutes, 15-1440
sf heartbeat <thesis-id> --x-interval 240        # minutes, 60-1440
sf heartbeat <thesis-id> --model base            # cheap | base | medium | heavy
sf heartbeat <thesis-id> --budget 15             # USD per month, 0 = unlimited

# pause / resume
sf heartbeat <thesis-id> --pause
sf heartbeat <thesis-id> --resume

# closed-loop intent creation
sf heartbeat <thesis-id> --closed-loop-entry
sf heartbeat <thesis-id> --no-closed-loop-entry
sf heartbeat <thesis-id> --closed-loop-exit
sf heartbeat <thesis-id> --no-closed-loop-exit
```

## API

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

Auth: `Authorization: Bearer sf_live_...` (or browser session). 404 if the thesis isn't owned by the caller.

### GET response

```json theme={null}
{
  "thesisId": "thesis_abc123",
  "config": {
    "mode": "active",
    "newsIntervalMin": 240,
    "xIntervalMin": 240,
    "evalModelTier": "cheap",
    "monthlyBudgetUsd": 15,
    "paused": false,
    "smartModel": true,
    "closedLoop": { "entry": false, "exit": false }
  },
  "defaults": { "newsIntervalMin": 240, "xIntervalMin": 240, "evalModelTier": "cheap", "monthlyBudgetUsd": 15, "smartModel": true, "...": "..." },
  "costs": {
    "monthlyTotal": 2.4831,
    "llmCalls": 18,
    "searchCalls": 6,
    "inputTokens": 84210,
    "outputTokens": 11034,
    "budgetRemaining": 12.5169
  }
}
```

### PATCH body

All fields optional. Any combination is accepted; unknown fields are ignored. `400 No valid fields to update` if nothing recognised is sent.

| Field              | Type              | Default                         | Range / values                                         | Notes                                                                                                                                                                                         |
| ------------------ | ----------------- | ------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `newsIntervalMin`  | number            | `240`                           | 15 – 1440                                              | Minutes between news/context scans.                                                                                                                                                           |
| `xIntervalMin`     | number            | `240`                           | 60 – 1440                                              | Minutes between social/X scans.                                                                                                                                                               |
| `evalModelTier`    | string            | `"cheap"`                       | `cheap`, `base`, `medium`, `heavy`                     | Model tier for the evaluation step.                                                                                                                                                           |
| `monthlyBudgetUsd` | number            | `15`                            | `>= 0` (`0` = unlimited)                               | Monthly LLM + search ceiling. Heartbeat auto-pauses when exceeded.                                                                                                                            |
| `paused`           | boolean           | `false`                         | —                                                      | Pauses the loop without changing thesis lifecycle. Server tags the pause as `manual` so it does not auto-resume next month.                                                                   |
| `smartModel`       | boolean           | `true`                          | —                                                      | Run `cheap` by default, auto-escalate to `medium` on a significant change (confidence delta > 3%, kill condition, or node prob shift > 15%). Forces a `medium` deep eval at least every 24 h. |
| `closedLoop`       | boolean \| object | `{ entry: false, exit: false }` | `true` ⇒ both, `false` ⇒ neither, or `{ entry, exit }` | Allow the monitor to create entry / exit intents. Booleans inside the object are individually validated.                                                                                      |

### PATCH response

```json theme={null}
{
  "thesisId": "thesis_abc123",
  "config": { "...": "merged config" },
  "updated": ["newsIntervalMin", "evalModelTier"]
}
```

### Errors

| Status | Body `error`                                                             | Cause                                                   |
| ------ | ------------------------------------------------------------------------ | ------------------------------------------------------- |
| `400`  | `newsIntervalMin must be 15-1440`                                        | Out-of-range value.                                     |
| `400`  | `xIntervalMin must be 60-1440`                                           | Out-of-range value.                                     |
| `400`  | `evalModelTier must be cheap, base, medium, or heavy`                    | Invalid tier.                                           |
| `400`  | `monthlyBudgetUsd must be >= 0`                                          | Negative budget.                                        |
| `400`  | `closedLoop must be boolean or { entry, exit }`                          | Wrong shape.                                            |
| `400`  | `closedLoop.entry must be boolean` / `closedLoop.exit must be boolean`   | Wrong member type.                                      |
| `400`  | `No valid fields to update`                                              | Body had no recognised fields.                          |
| `401`  | `Unauthorized. Provide a valid API key (Bearer sf_live_xxx) or session.` | Missing / invalid auth.                                 |
| `404`  | `Thesis not found`                                                       | Thesis does not exist or does not belong to the caller. |

### Curl

```bash theme={null}
# read
curl -H "Authorization: Bearer $SF_API_KEY" \
  "https://simplefunctions.dev/api/thesis/thesis_abc123/heartbeat"

# update
curl -X PATCH \
  -H "Authorization: Bearer $SF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"newsIntervalMin": 60, "evalModelTier": "base"}' \
  "https://simplefunctions.dev/api/thesis/thesis_abc123/heartbeat"
```

## What heartbeat is not

* Not an order router. It can write evaluations, signals, alerts, and (when `closedLoop` is enabled) execution intents. Orders still go through the intent / runtime / risk-gate path.
* Not the same as thesis lifecycle status. To archive, close, or publish a thesis, use the [Thesis API](/api-reference/thesis). To stop background evaluation only, set `paused: true` here.
* Not a global setting. Each thesis has its own heartbeat config, and the dashboard / CLI lets you tune them independently.

## Next steps

<CardGroup cols={2}>
  <Card title="Thesis API" href="/api-reference/thesis">
    Full thesis lifecycle HTTP surface.
  </Card>

  <Card title="Portfolio autopilot" href="/guides/portfolio-autopilot">
    Separate portfolio-tick loop and risk gates.
  </Card>
</CardGroup>
