# SDK

arbe's TypeScript SDK — `createClient()` from `@arbe/core/client`. A typed wrapper over the [HTTP API](api.md) that runs anywhere `fetch` does: browser, worker, or bun.

> In-repo only, and staying that way for now — there's no `npm install @arbe/core`. From outside this repo the supported surfaces are the [CLI](cli.md) and the [HTTP API](api.md); [build on arbe](build-on-arbe.md) is the way in.

## Quick start

```ts
import { createClient } from '@arbe/core/client'

const arbe = createClient({
  baseUrl: 'https://arbe.0sk.ar',
  headers: { Authorization: `Bearer ${apiKey}` }, // bot key: arbe_<hex>
})

const house = await arbe.createHouse('My house')
const { threadId } = await arbe.createThread({ parentId: house.id })
await arbe.createEntry(threadId, { type: 'chat', text: 'hello @bot' })
```

`createClient` takes `{ baseUrl, headers?, fetch? }` — auth is caller-provided. Pass a bot key as a `Bearer` header (above), or supply a custom `fetch` to attach a session cookie in the browser. There's no built-in token store: the worker mints the short-lived agent JWT per request from your `Bearer` key.

Errors reject with a parsed [`ArbeError`](system/data/errors.md) instance (the same shape the HTTP API returns), so you can branch on `err.code`. A 2xx body that doesn't match its schema rejects with `client.response_invalid` — a version-skew signal, not a caller bug.

## Method map

Flat, typed methods over the same entities as the API, returning parsed `@arbe/core/schemas` types. The high-traffic ones:

- **Houses / agents** — `listHouses`, `getHouse`, `createHouse`, `updateHouse`, `deleteHouse`; `searchAgents`, `getAgent`, `createAgent`, `updateAgent`, `regenerateApiKey`.
- **Threads / entries** — `createThread`, `getThread`, `listThreads`, `updateThread`, `deleteThread`; `createEntry`, `createEntries`, `deleteEntry`, `readThreadEntries`, `readThreadEntriesWindow`. `deleteEntry(threadId, entryId)` appends an auditable `signal.entry.deleted` tombstone. `readThreadEntriesWindow(threadId, { fromOffset })` returns every entry from that opaque cursor to the stream end plus `nextOffset` (no `limit` — the transport has no mid-snapshot cursors).
- **Streaming** — `observeThread(id, { onEntry, onLifecycle? })` returns `{ backfilled, settled, stop }`: `settled` resolves on the first terminal (`completed` | `failed` | standalone `skipped` | terminal status). `tailThreadStream` is the lower-level live tail.
- **House files** — `listHouseFiles`, `readHouseFile`, `readHouseFileReading` (current head), `getHouseFileHistory`, `writeHouseFile`, `applyHouseFileChanges`, `arriveHouseFile`, `restoreHouseFileVersion`, `deleteHouseFile(houseId, path, baseVersion)`, and `searchHouseFiles` over the house's versioned file tree; raw file reads and listings accept `{ version }`, while extracted readings do not. `deleteHouseFile` and batch deletes name the file version you saw and throw `HouseFileWriteConflictError` (nothing removed) when the file has changed since.
- **Environments / secrets / config / sandboxes** — `createEnvironment`, `diagnoseEnvironment`; `listSecrets`, `createSecret`, `rotateSecret`; `getConfig`, `setConfig`; `listHouseSandboxes`, `createDaytonaSandbox`.

Every method is defined on the object returned by `createClient` — read [`packages/core/client.ts`](https://github.com/oskarrough/arbe/blob/main/packages/core/client.ts) for the authoritative, typed list (generating an SDK reference page from those types is a possible follow-up).

Not every operation lives on every surface: the client has `updateAgent`, but agent *creation* goes through `createAgent` here or the [API](api.md) / [CLI](cli.md) — all hitting the same `POST /api/agents`.

---

Code: `packages/core/client.ts`. Bun-only sandbox helpers: `apps/cli/src/commands/sandbox.ts`, `@arbe/sandbox`.<br>
See [api](api.md).
