> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wardin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js

> @wardin/sdk — a typed TypeScript client for the Wardin control-plane REST API (keys, budgets, policies, providers, and the signed receipt / compliance evidence layer).

<Note>
  **Coming soon.** `@wardin/sdk` is not yet published to npm — the interface
  documented here is final and ships with the v1 launch. Until then `npm install
      @wardin/sdk` won't resolve.
</Note>

A typed TypeScript client for the Wardin control-plane REST API, with
resource-method ergonomics (`wardin.receipts.list()`). Every path, parameter, and
response is **generated from the same OpenAPI spec** that powers the
[API Reference](/api-reference/overview) — so the client can't drift from the
contract.

## Install

```sh theme={null}
npm install @wardin/sdk
```

**Requirements:** Node.js ≥ 18 (uses the global `fetch`). The package is
**ESM-only** (`import`, not `require`).

## Quickstart

```ts theme={null}
import { createWardinClient } from '@wardin/sdk';

const wardin = createWardinClient({ apiKey: process.env.WARDIN_API_KEY! }); // wardin_sk_...

// List virtual keys
const keys = await wardin.keys.list();

// Create a key with a monthly budget + rate limit
await wardin.keys.create({
  label: 'alice-dev',
  keyClass: 'interactive',
  budgetUsd: 50,
  rpmLimit: 60,
});

// Fetch a receipt — verified server-side (ED25519 signature + hash chain)
const receipt = await wardin.receipts.get(receiptId);
receipt.verified; // true

// Filter the signed receipt chain
const recent = await wardin.receipts.list({ actor: 'alice', provider: 'anthropic' });
```

## Authentication

Pass a secret API key (`wardin_sk_...`); it's sent as
`Authorization: Bearer`. Create one with
[`POST /v1/keys`](/api-reference/keys/create) or in the Console. Treat it as a
secret — **server-side only, never ship it to a browser.**

## Configuration

```ts theme={null}
createWardinClient({
  apiKey: process.env.WARDIN_API_KEY!,
  // Defaults to https://api.wardin.ai. Point at your own deployment, or
  // http://localhost:3000/api in local dev (the API serves the /api prefix).
  baseUrl: 'https://api.wardin.ai',
});
```

## Resources

Every method is fully typed (request body, params, response). On a non-2xx
response a method **throws `WardinApiError`**; on success it returns the parsed
body. Each method maps to a REST endpoint in the
[API Reference](/api-reference/overview).

| Resource                                                    | Methods                                                                               |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| [`wardin.keys`](/api-reference/keys/list)                   | `list()` · `create(body)` · `delete(id)` · `setLimits(id, body)` · `assign(id, body)` |
| [`wardin.budgets`](/api-reference/budgets/list)             | `list()` · `update(id, body)`                                                         |
| [`wardin.policies`](/api-reference/policies/list)           | `list()` · `create(body)` · `delete(id)`                                              |
| [`wardin.providers`](/api-reference/providers/list)         | `list()` · `test(provider)`                                                           |
| [`wardin.receipts`](/api-reference/receipts/list)           | `list(query?)` · `get(id)` · `verifyChain(id, query)` · `export(query?)` → `string`   |
| [`wardin.compliance`](/api-reference/compliance/frameworks) | `frameworks()` · `coverage(framework)` · `evidenceBundle(query?)`                     |

A budget is created together with a key (`keys.create` with `budgetUsd`) — there
is no standalone create, so `budgets` exposes only `list` + `update`.
`receipts.export(query?)` returns the matched set as a **raw JSONL/CSV string**
(pass `{ format: 'jsonl' | 'csv' }` plus the same filters as `receipts.list`).

## Common operations

**Create a virtual key** with a monthly budget and rate limit:

```ts theme={null}
const { id } = await wardin.keys.create({
  label: 'ci-pipeline',
  keyClass: 'ci', // 'interactive' | 'ci'
  budgetUsd: 100,
  rpmLimit: 120,
});
```

**Filter the signed receipt chain** — every gateway request is a queryable receipt:

```ts theme={null}
const recent = await wardin.receipts.list({
  actor: 'alice',
  provider: 'anthropic',
  from: '2026-07-01T00:00:00Z',
  limit: 50,
});
```

**Export evidence for an auditor** — a filtered JSONL stream, or a self-contained,
offline-verifiable bundle:

```ts theme={null}
const jsonl = await wardin.receipts.export({ format: 'jsonl', from: '2026-07-01T00:00:00Z' });
const bundle = await wardin.compliance.evidenceBundle({ range: '30d' });
```

**Check framework coverage** across the receipt chain:

```ts theme={null}
const frameworks = await wardin.compliance.frameworks();
const euAiAct = await wardin.compliance.coverage('eu_ai_act');
```

**Verify a receipt** was signed and unbroken in the chain:

```ts theme={null}
const receipt = await wardin.receipts.get(receiptId);
if (!receipt.verified) throw new Error('receipt failed verification');
```

## Pagination

`receipts.list` is cursor-paginated, newest first. Each response returns
`nextCursor` for the following page (absent once you reach the end) — loop until
it's gone:

```ts theme={null}
let cursor: number | undefined;
const all = [];
do {
  const page = await wardin.receipts.list({ limit: 100, cursor });
  all.push(...page.receipts);
  cursor = page.nextCursor ?? undefined;
} while (cursor);
```

The same filters (`from`, `to`, `actor`, `provider`, `model`, `check`,
`upstreamStatus`) apply to every page. For a large historical pull, prefer
`receipts.export()` — it streams the whole matched set in one call.

## Error handling

```ts theme={null}
import { WardinApiError } from '@wardin/sdk';

try {
  await wardin.keys.create({ label: '', keyClass: 'interactive', budgetUsd: 50 });
} catch (e) {
  if (e instanceof WardinApiError) {
    console.error(e.status, e.body); // HTTP status + typed error body
  }
}
```

## Escape hatch — any endpoint

Any endpoint not wrapped as a resource method is reachable on **`wardin.raw`**,
the underlying [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) client. It
returns `{ data, error }` (it does **not** throw) and is fully typed:

```ts theme={null}
const { data, error } = await wardin.raw.GET('/v1/usage', {
  params: { query: { period: '2026-07' } },
});
```

## Offline evidence verification

This SDK drives the control plane. To verify a tenant's signed receipt chain
**offline, without trusting Wardin**, export an Evidence Bundle
(`wardin.compliance.evidenceBundle()`) and check it with the open-source
`wardin-verify` CLI — see [Offline Verification](/concepts/offline-verification).

## Versioning

The SDK follows semantic versioning. Its types are generated from the committed
OpenAPI spec (app DTOs → `openapi.json` → SDK), and a CI gate fails on any drift,
so a published version always matches the contract it was built against. Breaking
changes to the `/v1` contract ship as a new major with release notes.

## Other languages

TypeScript is the first SDK. Python and Go clients are planned — until then, any
language can call the [REST API](/api-reference/overview) directly, or generate a
client from the [OpenAPI spec](https://docs.wardin.ai/api-reference/openapi.json).
