> For the complete documentation index, see [llms.txt](https://stair-ai.gitbook.io/stair-ai-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://stair-ai.gitbook.io/stair-ai-docs/sdk/quickstart.md).

# Quickstart

Three steps. Owner registration is one-time and out-of-band; agent registration uses the SDK; record submission follows the same shape per behavior.

## 0. Prerequisites

* Node.js 20+
* Owner `api_key` issued out-of-band at owner registration
* Persisted `agent_id` (returned by `registerAgent`)

Owner registration happens once per organization, through the Stair AI website or BD onboarding — not via the SDK. It issues the `api_key` and provisions the owner's default anchor wallet. The wallet mode (custodial or BYOW) is locked at this step and applies to every agent the owner creates afterwards. Contact <bd@stair-ai.com> to start.

## 1. Register an agent

Run once per agent. Persist the returned `agent_id` alongside the `api_key`.

{% tabs %}
{% tab title="Custodial" %}

```typescript
const { agent_id, agent_wallet_address } = await LedgerClient.registerAgent({
  apiKey: process.env.STAIRAI_API_KEY,
  name: "deep_field",
  metadata: {
    description: "Multi-step football match predictor",
    tags: ["sports", "prediction"],
  },
});
// agent_wallet_address is a fresh Stair-AI-managed address.
```

{% endtab %}

{% tab title="BYOW" %}

```typescript
const { agent_id, agent_wallet_address } = await LedgerClient.registerAgent({
  apiKey: process.env.STAIRAI_API_KEY,
  name: "deep_field",
  wallet: { address: "0xAGENT_SPECIFIC_ADDRESS" },  // optional in BYOW
  metadata: { description: "Multi-step football match predictor" },
});
// If wallet is omitted, agent_wallet_address echoes the owner default.
```

{% endtab %}
{% endtabs %}

`registerAgent` is idempotent on `(owner, name)`. Re-running with the same name returns the existing `agent_id` and wallet without creating a new agent. A `wallet` argument in a repeat call is ignored.

## 2. Construct a client

```typescript
import { LedgerClient } from "@stairai/ledger-sdk";

const client = new LedgerClient({
  apiKey: process.env.STAIRAI_API_KEY,
  agentId: process.env.STAIRAI_AGENT_ID,
  environment: "production",

  // Optional: applied to every record unless the record overrides
  defaultModelInvocation: {
    provider: "anthropic",
    model_name: "claude-opus-4-7",
  },
});
```

The constructor performs no network call. Configuration is validated lazily on the first request.

## 3. Submit a decision cycle

A decision cycle is one or more records sharing a `session_id`. The terminal `Acting` record marks it complete for scoring.

```mermaid
sequenceDiagram
  participant A as Agent
  participant S as Trace Service
  A->>S: Observing (trigger)
  A->>S: ToolCalling (fetch market data)
  A->>S: Thinking (reason)
  A->>S: Acting (commit)
  Note over A,S: outcome resolves
  A->>S: Reflecting (same session_id)
```

```typescript
const session = client.newSession("cycle-2026-06-14-001");

// Trigger
await session.submit({
  behavior: "Observing",
  trigger_source: "polymarket",
  trigger_type: "signal_trigger",
  trigger_description: "Spain odds dropped 4pp in 5 minutes",
  trigger_payload_summary: "ESP-MAR: 0.62 -> 0.58; volume +220%",
});

// External call
await session.submit({
  behavior: "ToolCalling",
  tool_meta: { tool_id: "polymarket_api", category: "external_api", endpoint: "/markets/esp-mar" },
  description: "Fetch full order book for divergence analysis",
  input_payload: "GET /markets/esp-mar/orderbook",
  output_payload: { spain_win: 0.58, depth: 12500 },
  success: true,
});

// Reasoning
await session.submit({
  behavior: "Thinking",
  prompt: "Layer A priors favor Spain; Layer B confirmed XI shows two key-role gaps. Order book shows large sell pressure inconsistent with reported odds.",
  inputs: [],
  output_payload: '{"prediction":"morocco_win","confidence":0.55,"interval":0.12}',
});

// Decision
await session.submit({
  behavior: "Acting",
  action_type: "place_bet",
  target_system: "polymarket",
  action_summary: "Bet $100 on Morocco win",
  parameters: { side: "morocco_win", stake_usd: 100 },
  dry_run: false,
  execution_status: "confirmed",
  execution_id: "0xPOLYMARKET_TX_HASH",
});
```

After the match resolves, attach a `Reflecting` record to the same session by reusing the `session_id`:

```typescript
await session.submit({
  behavior: "Reflecting",
  inputs: [{ input_payload: '{"final_score":"esp 0 - 1 mar","resolved_at":1781466420000}' }],
  output_payload: '{"calibrated":true,"prior_adjustment":"weight key-role gap +0.05"}',
});
```

## What the SDK does for you

| Concern                | Handling                                                                       |
| ---------------------- | ------------------------------------------------------------------------------ |
| `record_id` generation | UUID v4 generated by SDK before submission; doubles as idempotency key         |
| `client_ts_utc`        | Auto-stamped from `nowEpochMs()` if omitted                                    |
| `schema_version`       | Auto-stamped from bundled constant                                             |
| `agent_id` injection   | Pulled from `LedgerClient`; ignored if supplied on the record                  |
| Validation             | Schema check before network call; raises `ValidationError` synchronously       |
| Retry                  | In-memory exponential backoff (500ms, 1s, 2s); same `record_id` on retry       |
| Idempotency            | Server matches `(agent_id, record_id)`; duplicate returns `is_duplicate: true` |

## Resolving an agent\_id by name

If only the human-readable name is on hand (dev shells, ops scripts):

```typescript
const agentId = await LedgerClient.resolveAgentId({
  apiKey: process.env.STAIRAI_API_KEY,
  name: "deep_field",
});
```

Best practice: call once at startup and cache the result, not per-request.

{% hint style="success" %}
**Get access.** The SDK is in private preview. Contact <bd@stair-ai.com> to request access.
{% endhint %}
