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

# TypeScript SDK

> Install and use the @spfunctions/sdk package.

`@spfunctions/sdk` is the TypeScript data and contract client for SimpleFunctions.

It is now published as a stable package:

```bash theme={null}
npm install @spfunctions/sdk@1.0.1
```

Use the stable 1.0.1 package in server-side TypeScript services, agents, and internal tools.

## What The SDK Is

The SDK is a typed wrapper over stable SimpleFunctions HTTP objects and the strict contract manifest.

Use it when you want to build:

* market-intelligence dashboards
* internal research tools
* macro policy monitors
* election and legislation workflows
* portfolio and thesis review tools
* Agent SDK runtimes that need an identity-bearing client

The SDK is not:

* the CLI
* the Agent SDK
* an MCP client
* a browser runtime for long-lived API keys
* a trading engine
* a wrapper around every SimpleFunctions API route

## Install

```bash theme={null}
npm install @spfunctions/sdk@1.0.1
```

Node 18 or newer is required because the SDK uses the platform `fetch` API.

## Authentication

Most useful SDK calls require a SimpleFunctions API key.

```bash theme={null}
export SF_API_KEY="sf_..."
```

Then construct the client:

```ts theme={null}
import { SimpleFunctions } from "@spfunctions/sdk"

const sf = new SimpleFunctions({
  baseUrl: "https://simplefunctions.dev",
  apiKey: process.env.SF_API_KEY,
})
```

The constructor also reads `process.env.SF_API_KEY` and `process.env.SF_API_URL` when explicit options are omitted.

Do not expose a long-lived `SF_API_KEY` in browser code. Use the SDK from a server process, worker, backend job, notebook, or local agent harness.

## No-key Bootstrap

The SDK is API-key-first, but strict manifest inspection works without a key:

```ts theme={null}
const sf = new SimpleFunctions({ baseUrl: "https://simplefunctions.dev" })

const manifest = await sf.manifest.list()
const worldTool = await sf.manifest.get("world.read")
const legacyName = await sf.manifest.get("get_world_state")

console.log(manifest.schemaVersion) // "0.3.0-draft"
console.log(worldTool?.name)        // "world.read"
console.log(legacyName)             // null
```

No-key bootstrap is intentionally narrow. Data, research, user-data, and cost-bearing calls are preflighted against `/api/contracts/tools` metadata. If the contract has `costEffect !== "none"`, `sideEffect !== "none"`, `authRequired: true`, `user_data` permission, or `access.anonymousAllowed: false`, the SDK throws `MissingApiKeyError` before making the live request.

## First Data Call

```ts theme={null}
import { SimpleFunctions } from "@spfunctions/sdk"

const sf = new SimpleFunctions({
  baseUrl: "https://simplefunctions.dev",
  apiKey: process.env.SF_API_KEY,
})

const world = await sf.world.get()

console.log(world.asOf)
console.log(world.regime?.label)
console.log(world.salient?.map(item => item.label))
```

`world.read` is a real hosted data call. Its strict contract currently has:

```json theme={null}
{
  "name": "world.read",
  "sideEffect": "none",
  "costEffect": "api_cost",
  "access": { "anonymousAllowed": false }
}
```

So `new SimpleFunctions().world.get()` should fail with `MissingApiKeyError` unless `SF_API_KEY` is available in the environment.

## Read And Research Surfaces

```ts theme={null}
await sf.world.get()
await sf.world.delta({ since: "1h" })

await sf.markets.search({
  query: "Fed CPI",
  venue: "kalshi",
  limit: 10,
})

await sf.markets.discover({
  query: "oil geopolitics",
  limit: 5,
})

await sf.markets.get("KXRECESSION-26DEC31")
await sf.markets.history("KXRECESSION-26DEC31")

await sf.query.ask({
  q: "What are prediction markets saying about Fed cuts?",
})

await sf.econ.query({
  q: "unemployment rate",
  mode: "raw",
})

await sf.gov.query({
  q: "SAVE Act",
  mode: "raw",
})
```

These are read or research calls with `sideEffect: "none"`. They still require identity when the strict contract marks them as cost-bearing.

## Market Intelligence Surfaces

The SDK also exposes existing market-intelligence API surfaces through `sf.intelligence.*`. These are the pieces a fund, macro desk, election office, or internal research team usually needs before they build an application: screeners, regime scans, calendars, index history, contagion signals, cross-venue pairs, yield curves, and calibration summaries.

```ts theme={null}
await sf.intelligence.screen({
  iyMin: 20,
  limit: 10,
  nextActions: false,
})

await sf.intelligence.screenByTickers({
  tickers: ["KXEXAMPLE"],
  sort: "iy",
})

await sf.intelligence.regime({ label: "toxic", limit: 5 })
await sf.intelligence.calendar({ days: 14, category: "Economic Data" })
await sf.intelligence.index()
await sf.intelligence.indexHistory({ days: 30 })
await sf.intelligence.contagion({ window: "6h" })
await sf.intelligence.crossVenuePairs({ preset: "arb", limit: 10 })
await sf.intelligence.crossVenueStats()
await sf.intelligence.yieldCurves({ compact: true })
await sf.intelligence.calibration({ period: "30d" })
```

Trader-style market data lives under `sf.markets.*`:

```ts theme={null}
const candles = await sf.markets.candles("KXBTCD-26MAY1917-T76499.99", {
  venue: "kalshi",
  timeframe: "1m",
  limit: 500,
})

const movers = await sf.markets.screenCandles({
  venue: "kalshi",
  tickers: ["KXBTCD-26MAY1917-T76499.99", "KXWTI-26MAY1914-T104.99"],
  timeframes: ["1m", "5m", "15m", "1h"],
  minBars: 20,
  minAbsReturnPct: 2,
  minRangePct: 3,
  concurrency: 4,
  sort: "score",
})
```

`sf.markets.candles()` maps to `market.candles` and returns OHLCV/K-line bars for `1m`, `5m`, `15m`, `1h`, and `1d`. Pass `venue: "kalshi"` or `venue: "polymarket"` when the market id is ambiguous. `screenCandles()` is a bounded-concurrency client-side helper for watchlist-first momentum, range, volatility, and volume screening.

These calls still follow the API-key-first contract. They are read-only, but they are real hosted data and analytics calls, so no-key callers get `MissingApiKeyError` before the SDK sends a request.

## Data Routing

The SDK default `baseUrl` is `https://simplefunctions.dev`. Agent SDK tools use the same SDK client. That means contract-info reads such as `sf.markets.get()` and `agent.call("market.inspect", ...)` hit the Vercel API surface first.

From there, routing is endpoint-specific:

* `market.inspect` runs on the SimpleFunctions API server and returns price, spread, orderbook depth levels, and liquidity score. The server may read DB/cache rows, use the configured Kalshi orderbook proxy, fall back to server-side Kalshi credentials, or call Polymarket CLOB.
* `market.candles` hits the Vercel API route, then proxies to the terminal/Fly candle service behind `TERMINAL_BASE`.
* The SDK does not call Kalshi, Polymarket, the CLI, or a local runtime directly unless you set `baseUrl` or `SF_API_URL` to a local/self-hosted SimpleFunctions API.

## Authenticated User Reads

```ts theme={null}
await sf.theses.list()
await sf.theses.get("thesis-id")

await sf.portfolio.state()
await sf.portfolio.ticks.list({ limit: 5, envelope: true })
await sf.portfolio.trades.list({ limit: 5, envelope: true })

await sf.intents.list({ active: true })
await sf.watchlists.list()
await sf.alerts.list()
await sf.alerts.create({
  watch: "KXFED-27APR-T3.50",
  type: "price_above",
  threshold: 60,
  idempotencyKey: "agent-run-123:fed-alert",
})
```

These calls require `SF_API_KEY` because they are account-scoped user-data reads or governed user writes.

## User Writes In 1.0

The SDK includes thesis write methods:

```ts theme={null}
const thesis = await sf.theses.create({
  title: "Fed cuts by September",
  statement: "The target range will be lower after the September meeting.",
})

await sf.theses.signal(thesis.id, {
  type: "evidence",
  content: "New CPI print moved the relevant markets.",
})
```

These methods are not enabled as default Agent-callable tools. Their strict contracts are implemented, authenticated, and marked:

```text theme={null}
sideEffect: user_write
costEffect: api_cost
agent.callable: false
```

## Request Metadata

SDK responses carry source metadata where the API provides it:

```ts theme={null}
const world = await sf.world.get()

console.log(world.source?.endpoint)
console.log(world.source?.requestId)
console.log(world.source?.traceId)
```

Typed errors also carry request metadata:

```ts theme={null}
import {
  MissingApiKeyError,
  PermissionDeniedError,
  SimpleFunctionsRateLimitError,
} from "@spfunctions/sdk"

try {
  await sf.theses.list()
} catch (error) {
  if (error instanceof MissingApiKeyError) {
    console.error(error.code, error.tool, error.reason)
  } else if (error instanceof PermissionDeniedError) {
    console.error(error.status, error.requestId)
  } else if (error instanceof SimpleFunctionsRateLimitError) {
    console.error("retry later", error.status)
  } else {
    throw error
  }
}
```

The SDK exports typed errors for missing or invalid keys, permission failures, validation failures, not-found responses, conflicts, rate limits, upstream failures, invalid JSON, and timeouts.

## Pagination Helpers

Some authenticated reads return SimpleFunctions page envelopes:

```ts theme={null}
import { getNextCursor, hasMore } from "@spfunctions/sdk"

let page = await sf.intents.list({ limit: 5 })

while (hasMore(page)) {
  page = await sf.intents.list({
    limit: 5,
    cursor: getNextCursor(page) ?? undefined,
  })
}
```

## Contract Truth

The SDK uses `/api/contracts/tools` as its strict truth source.

```bash theme={null}
curl https://simplefunctions.dev/api/contracts/tools
```

Current production manifest:

```text theme={null}
schemaVersion: 0.3.0-draft
active implemented tools: 32
Agent-callable read tools: 30
implemented but not Agent-callable writes: theses.create, theses.signal
```

Use canonical dotted names such as `world.read`, `markets.search`, and `market.inspect`.

Do not treat `/api/tools` names such as `get_world_state` as SDK contract names. `/api/tools` is the broader hosted compatibility inventory.

## Boundary With CLI And Agent SDK

| Surface                 | Use it for                                            |
| ----------------------- | ----------------------------------------------------- |
| `@spfunctions/sdk`      | Typed data, user reads/writes, contract inspection    |
| `@spfunctions/agent`    | Cursor-style market-intelligence agent runtime        |
| `@spfunctions/agent/v1` | Compatibility subpath for the same model-loop exports |
| `sf agent --tool`       | Command-line direct tool wrapper                      |
| `/api/contracts/tools`  | Canonical SDK/Agent truth                             |
| `/api/tools`            | Broad hosted compatibility inventory                  |
| MCP server              | Broad agent-client adapter, not SDK truth             |

## Execution Surfaces

Kalshi and Polymarket execution are exposed through explicit, governed contract tools:

```ts theme={null}
await sf.execution.place({
  ticker: "KXFED-27APR-T3.50",
  action: "buy",
  quantity: 1,
  limitPrice: 32,
  runtime: { startIfNeeded: true },
})

await sf.runtime.status()
await sf.runtime.ensure()

await sf.intents.create({
  action: "buy",
  venue: "kalshi",
  marketId: "KXFED-27APR-T3.50",
  marketTitle: "Fed target rate",
  direction: "yes",
  targetQuantity: 1,
  maxPrice: 32,
  autoExecute: true,
})

await sf.execution.place({
  venue: "polymarket",
  tokenId: "POLYMARKET_CLOB_TOKEN_ID",
  marketTitle: "Polymarket event outcome",
  action: "buy",
  quantity: 1,
  limitPrice: 32,
  runtime: { startIfNeeded: true },
})
```

`execution.place` checks cloud and configured SDK runtime candidates before creating the intent. Hosted cloud status is daemon-aware: a Fly machine that is started but has no live runtime daemon is not treated as executable, so `ensure` starts or wakes the daemon when allowed. Polymarket execution requires a CLOB token id and an explicit limit price; venue signing is handled by the runtime with user-configured exchange credentials. Set `runtime: { mode: "none" }` only for an explicit intent-only workflow. The SDK package does not depend on the CLI package.

Agent SDK callers must opt in through policy before these tools are callable, for example `maxSideEffect: "live_trade"`, `maxCostEffect: "venue_request_cost"`, and `trade` guardrails such as venue/ticker allowlists, blocked venues, jurisdiction requirements, max quantity, max order cost, required limit prices, confirmation tokens, and `allowRuntimeStart: false` when runtime startup must be operator-controlled.

## Not In SDK 1.0

The SDK does not include:

* browser long-lived API keys
* every CLI command
* every `/api/public/*` route
* every MCP tool
* `events.*`
* `market.related`
* `auth.status`
* `investigations.create`
* `intents.propose`
* `webhooks.create`

Those surfaces require separate contract, auth, side-effect, cost, and policy decisions before becoming SDK or Agent SDK surfaces.
