<p align="center">
  <a href="https://railx.dev">
    <img src="https://railx.dev/railx-avatar.png" alt="RailX" width="88" height="88" />
  </a>
</p>

# `@railx/sdk`

Put the stock market in your app. RailX is the trading API for tokenized stocks on Robinhood Chain, built for apps, wallets, bots, and AI agents.

The official TypeScript SDK resolves Stock Tokens, finds routes, checks project policy, builds every required transaction, and simulates the full sequence. Your app gets an explainable verdict and an unsigned plan. Your wallet signs.

[Documentation](https://railx.dev/docs) · [OpenAPI](https://railx.dev/api/v0/openapi) · [npm](https://www.npmjs.com/package/@railx/sdk) · [Issues](https://github.com/railxdev/sdk/issues)

Use `@railx/sdk` to add Robinhood Chain market data and Stock Token trading to:

- investing apps and embedded stock experiences;
- wallets, portfolio trackers, and trading bots;
- TypeScript backends that need quotes or checked trade plans; and
- AI agents that need structured market context and unsigned transactions.

## Install

```bash
npm install @railx/sdk
```

Node.js 20.3 or newer is required. The SDK targets API `v0`; SDK releases use their own semantic versions.

Create a free test key in the [RailX dashboard](https://railx.dev/dashboard). Test keys use a deterministic sandbox, so you can try the complete API without funds or provider credentials.

## Plan a trade in the sandbox

Create a test key in the [dashboard](https://railx.dev/dashboard), set `RAILX_PROJECT_KEY`, and use the funded fixture wallet below. This moves no funds. Use a live key and your own wallet only when ready to leave the sandbox.

```ts
import { RailXClient } from '@railx/sdk';

const railx = new RailXClient({
  baseUrl: 'https://railx.dev/api/v0',
  apiKey: process.env.RAILX_PROJECT_KEY,
});

const plan = await railx.trades.plan({
  wallet: '0x0000000000000000000000000000000000000001',
  intent: {
    type: 'exact_input',
    tokenIn: 'USDG',
    tokenOut: 'NVDA',
    amountIn: '500',
  },
});

console.log(plan.verdict);
console.log(plan.signing.unsignedTransactions);
```

RailX never holds funds or signs transactions. `allow` returns a `ready` plan. `warn` returns `review_required` with transactions for human review. `block` returns no signable transactions.

## What the SDK covers

- Official Stock Token resolution and reference prices
- Wallet balances and portfolio context
- Corporate actions
- Exact-input and exact-output quotes
- Durable, idempotent trade plans
- Project policy with `allow`, `warn`, and `block` verdicts
- Full-sequence onchain simulation
- A deterministic test-key sandbox

Every request and response type is exported from the package root and from `@railx/sdk/types`. The API and SDK share the same contract source. Configure project policy in the dashboard; the SDK returns the policy version, checks, and verdict with each plan.

## Use the free sandbox

A `railx_test_` key never calls a quote provider or the chain. It returns repeatable fixtures, so local development and CI do not need provider credentials or funded wallets.

```ts
const sandbox = new RailXClient({
  baseUrl: 'https://railx.dev/api/v0',
  apiKey: process.env.RAILX_TEST_KEY,
  scenario: 'simulation_failed',
});
```

See the [sandbox fixtures](https://railx.dev/docs#sandbox) for every wallet and outcome.

## Add RailX tools to an AI agent

Use the official [`@railx/mcp`](https://www.npmjs.com/package/@railx/mcp) server to expose the same market, portfolio, quote, planning, and simulation operations to Claude, Cursor, and other MCP clients.

```bash
npx -y @railx/mcp
```

See the [agent setup guide](https://railx.dev/docs#agents) for the complete MCP configuration.

## Timeouts and cancellation

Each attempt, including reading its response body, times out after 30 seconds by default. Set a client default, override one call, or cancel it with an `AbortController`.

```ts
const railx = new RailXClient({
  baseUrl: 'https://railx.dev/api/v0',
  apiKey: process.env.RAILX_PROJECT_KEY,
  timeoutMs: 10_000,
});

const controller = new AbortController();

const price = await railx.prices.get('NVDA', {
  signal: controller.signal,
  timeoutMs: 5_000,
});
```

Set `timeoutMs: 0` to disable the timeout.

## Errors and retries

API failures and malformed response envelopes throw `RailXApiError` with `status`, `code`, `retryable`, `details`, and `requestId`. Timeouts use `REQUEST_TIMEOUT`; caller cancellation uses `REQUEST_ABORTED` and is never retried.

Safe GET requests retry retryable failures up to `retries` times (default 2). Quote and simulation POST requests are never retried. Trade-plan creation is retried safely with the same idempotency key, so one intent cannot create duplicate plans.

Use a non-negative integer for `retries`. Pass `{ idempotencyKey: 'your-unique-trade-id' }` as the second argument to `trades.plan()` to reuse an intent across separate calls. Keys are 16–128 URL-safe characters; reuse a key only for the same intent, and create a new key after its plan expires.

```ts
import { RailXApiError } from '@railx/sdk';

try {
  await railx.assets.get('UNKNOWN');
} catch (error) {
  if (error instanceof RailXApiError) {
    console.error(error.code, error.requestId);
  }
}
```

`railx.lastResponse` exposes the request ID, environment, plan, sandbox state, and rate-limit metadata from the most recent response.

## License

MIT
