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

# TypeScript SDK

> Install and use @anomalyarmor/sdk for TypeScript and JavaScript

<div aria-hidden="true" style={{position:"absolute",width:"1px",height:"1px",overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap"}}>For LLM agents: documentation index at <a href="/llms.txt" tabIndex={-1}>/llms.txt</a>, full text at <a href="/llms-full.txt" tabIndex={-1}>/llms-full.txt</a>. Append .md to any page URL for plain markdown.</div>
`@anomalyarmor/sdk` is the official TypeScript SDK for AnomalyArmor. Types are generated straight from our OpenAPI spec via `openapi-typescript`, so every endpoint is typed end-to-end. Runtime is a thin wrapper around `openapi-fetch` with Bearer auth and retry-on-429 middleware baked in.

## Install

```bash theme={null}
npm install @anomalyarmor/sdk
# or: pnpm add @anomalyarmor/sdk  /  yarn add @anomalyarmor/sdk
```

Node 18+ is required. The package ships dual ESM + CJS builds and TypeScript types.

## Quickstart

```ts theme={null}
import { createAnomalyArmorClient } from '@anomalyarmor/sdk';

const client = createAnomalyArmorClient({
  apiKey: process.env.ANOMALYARMOR_API_KEY!, // aa_live_...
});

// Liveness probe — 200 means key is valid
const health = await client.health.check();

// Aggregate counts of open alerts
const overview = await client.alerts.overview();
console.log(`${overview.unresolved_alerts} unresolved alerts`);

// Per-table freshness for an asset
const freshness = await client.freshness.check('asset-uuid-here');
```

Get an API key at [app.anomalyarmor.ai/settings/api-keys](https://app.anomalyarmor.ai/settings/api-keys). Keys start with `aa_live_`.

## Authentication

The SDK captures your API key at construction time and attaches `Authorization: Bearer <key>` to every outgoing request via a small middleware. **The SDK library itself never reads from `process.env`** - that would hide auth at a distance. Only the CLI shim (next section) reads `ANOMALYARMOR_API_KEY`, because CLIs traditionally do.

<Frame>
  <img src="https://mintcdn.com/anomalyarmor/LtMAQHyaUI5VsBAf/images/diagrams/sdk-bearer-auth-flow-light.svg?fit=max&auto=format&n=LtMAQHyaUI5VsBAf&q=85&s=96829871a18352cef7c1a3b03c7c54cb" alt="Bearer auth flow" className="block dark:hidden" width="720" height="340" data-path="images/diagrams/sdk-bearer-auth-flow-light.svg" />

  <img src="https://mintcdn.com/anomalyarmor/LtMAQHyaUI5VsBAf/images/diagrams/sdk-bearer-auth-flow-dark.svg?fit=max&auto=format&n=LtMAQHyaUI5VsBAf&q=85&s=79630c0a54cc526ef159709c1a3c3b1b" alt="Bearer auth flow" className="hidden dark:block" width="720" height="340" data-path="images/diagrams/sdk-bearer-auth-flow-dark.svg" />
</Frame>

## CLI

The package ships a `bin` binary so you can smoke-check connectivity without writing code:

```bash theme={null}
# With the env var set:
export ANOMALYARMOR_API_KEY=aa_live_...
npx anomalyarmor health

# Or pass the key directly:
npx anomalyarmor health --api-key aa_live_...
```

The CLI has exactly one command (`health`) by design - anything richer belongs in a real script calling the library API.

## Rate limiting & retries

The SDK automatically retries HTTP 429 responses, honoring `Retry-After`. Retries are bounded (default 3 attempts, each sleep capped at 60 s) and only apply to idempotent verbs (`GET`, `HEAD`, `PUT`, `DELETE`, `OPTIONS`) so a `POST` failure never duplicates a side effect.

<Frame>
  <img src="https://mintcdn.com/anomalyarmor/LtMAQHyaUI5VsBAf/images/diagrams/sdk-retry-on-429-flow-light.svg?fit=max&auto=format&n=LtMAQHyaUI5VsBAf&q=85&s=cb66c21a5760d741899eca0980f36386" alt="Retry-on-429 flow" className="block dark:hidden" width="720" height="360" data-path="images/diagrams/sdk-retry-on-429-flow-light.svg" />

  <img src="https://mintcdn.com/anomalyarmor/LtMAQHyaUI5VsBAf/images/diagrams/sdk-retry-on-429-flow-dark.svg?fit=max&auto=format&n=LtMAQHyaUI5VsBAf&q=85&s=cf5663935dbfb4336bbbab9d932de8d4" alt="Retry-on-429 flow" className="hidden dark:block" width="720" height="360" data-path="images/diagrams/sdk-retry-on-429-flow-dark.svg" />
</Frame>

Tune or disable:

```ts theme={null}
const client = createAnomalyArmorClient({
  apiKey: '...',
  maxRetries: 5,              // default 3
  maxRetrySleepSeconds: 120,  // default 60
});

// Opt out of retries entirely (mutation-heavy callers who want explicit control):
const client = createAnomalyArmorClient({ apiKey: '...', maxRetries: 0 });
```

## How requests work

Every typed SDK call flows through six layers: the ergonomic helper → `openapi-fetch` → the middleware stack → platform `fetch` → parse / unwrap → a statically-typed response.

<Frame>
  <img src="https://mintcdn.com/anomalyarmor/LtMAQHyaUI5VsBAf/images/diagrams/sdk-request-lifecycle-diagram-light.svg?fit=max&auto=format&n=LtMAQHyaUI5VsBAf&q=85&s=dfe28856eec61b09e5984f377b090a74" alt="Request lifecycle" className="block dark:hidden" width="720" height="320" data-path="images/diagrams/sdk-request-lifecycle-diagram-light.svg" />

  <img src="https://mintcdn.com/anomalyarmor/LtMAQHyaUI5VsBAf/images/diagrams/sdk-request-lifecycle-diagram-dark.svg?fit=max&auto=format&n=LtMAQHyaUI5VsBAf&q=85&s=51a58fd36753d9aa39d47eb393a893cc" alt="Request lifecycle" className="hidden dark:block" width="720" height="320" data-path="images/diagrams/sdk-request-lifecycle-diagram-dark.svg" />
</Frame>

## Error handling

Ergonomic methods throw `AnomalyArmorApiError` on 4xx / 5xx responses:

```ts theme={null}
import { AnomalyArmorApiError } from '@anomalyarmor/sdk';

try {
  await client.alerts.get('does-not-exist');
} catch (err) {
  if (err instanceof AnomalyArmorApiError) {
    console.error(`HTTP ${err.status}: ${err.message}`);
    console.error('Full body:', err.body);
  } else {
    throw err; // network / parse / programmer errors
  }
}
```

## Drop down to the raw client

The ergonomic surface covers the endpoints customers use most often. For anything else, `client.raw` exposes the full typed `openapi-fetch` client:

```ts theme={null}
const { data, error } = await client.raw.GET('/api/v1/assets/{asset_id}', {
  params: { path: { asset_id: 'my-asset' } },
});
if (error) throw new Error(`asset lookup failed: ${error.message}`);
// data is typed from the OpenAPI spec
```

## Configuration reference

| Option                 | Default                       | Purpose                                            |
| ---------------------- | ----------------------------- | -------------------------------------------------- |
| `apiKey`               | *(required)*                  | Your `aa_live_*` Bearer token.                     |
| `baseUrl`              | `https://app.anomalyarmor.ai` | Override for staging or a local backend.           |
| `maxRetries`           | `3`                           | 429-retry budget. Set to `0` to disable.           |
| `maxRetrySleepSeconds` | `60`                          | Cap on any single `Retry-After` sleep.             |
| `fetch`                | `globalThis.fetch`            | Injectable `fetch` for tests / alternate runtimes. |

## Next steps

<CardGroup cols={2}>
  <Card title="API reference" icon="book-open" href="/sdk/javascript-reference">
    Full typed reference for every SDK method
  </Card>

  <Card title="Authentication" icon="key" href="/api/authentication">
    How API keys work across the platform
  </Card>

  <Card title="Python SDK" icon="python" href="/sdk/overview">
    Same API surface in Python
  </Card>

  <Card title="CLI reference" icon="terminal" href="/cli/overview">
    Installable CLI for interactive use
  </Card>
</CardGroup>

## Common Questions

### What Node version does the SDK support?

Node 18 or higher, so `globalThis.fetch` is available natively. The package ships dual ESM + CJS builds and its own TypeScript types, so it drops into Next.js, Vite, and plain Node projects without extra polyfills.

### Does the SDK retry failed requests automatically?

Yes, but only for idempotent verbs (`GET`, `HEAD`, `PUT`, `DELETE`, `OPTIONS`) and only on HTTP 429. It honors the `Retry-After` header with a 3-attempt default, each sleep capped at 60 seconds. Tune with `maxRetries` / `maxRetrySleepSeconds`, or pass `maxRetries: 0` to opt out when you want explicit control of mutation retries.

### How do I call an endpoint that isn't in client.alerts / client.freshness / client.schema?

Drop down to `client.raw`, which is a fully typed `openapi-fetch` client covering the whole OpenAPI surface. You get the same Bearer auth and retry middleware, plus path/query/body types generated from the spec. Example: `client.raw.GET('/api/v1/assets/{asset_id}', { params: { path: { asset_id } } })`.

### How do I point the SDK at a local backend or staging environment?

Pass `baseUrl` at construction time: `createAnomalyArmorClient({ apiKey, baseUrl: 'http://localhost:8000' })`. The default is `https://app.anomalyarmor.ai`. For testing, you can also inject a custom `fetch` via the `fetch` option to stub responses without going over the network.

### Why does the SDK library ignore ANOMALYARMOR\_API\_KEY?

Intentional: library code must receive `apiKey` explicitly so authentication isn't hidden at a distance, which makes multi-tenant and per-request key rotation safe. Only the `npx anomalyarmor` CLI shim reads `ANOMALYARMOR_API_KEY`, matching standard CLI conventions.
