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

# TypeScript SDK

> Official TypeScript SDK for the Signa API

The `@signa-so/sdk` package is a typed, ergonomic client for the Signa API: full types for every endpoint and response, automatic pagination, built-in retries, and typed error classes.

<Note>
  The SDK is designed for **server-side use**. A Signa API key grants full access to your org's data; putting one in browser code exposes it to every visitor. Proxy requests through your own backend instead.
</Note>

## Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @signa-so/sdk
  ```

  ```bash yarn theme={null}
  yarn add @signa-so/sdk
  ```

  ```bash pnpm theme={null}
  pnpm add @signa-so/sdk
  ```

  ```bash bun theme={null}
  bun add @signa-so/sdk
  ```
</CodeGroup>

Requires Node.js 18+ or Bun 1.0+. TypeScript 5.0+ is recommended but not required.

## Configure

```typescript theme={null}
import { Signa } from '@signa-so/sdk';

const signa = new Signa({
  api_key: process.env.SIGNA_API_KEY,
  // Optional overrides
  base_url: 'https://api.signa.so', // default
  timeout: 30_000,                  // 30s default
  max_retries: 2,                   // default, retries on 429/5xx
});
```

If you omit `api_key`, the client reads `SIGNA_API_KEY` from the environment:

```bash theme={null}
export SIGNA_API_KEY=sig_your_key_here
```

```typescript theme={null}
// Uses SIGNA_API_KEY automatically
const signa = new Signa();
```

## Resources

The client organizes the API into 19 resource namespaces:

| Namespace             | Description                                                                                       |
| --------------------- | ------------------------------------------------------------------------------------------------- |
| `signa.trademarks`    | Search, retrieve, history, batch lookup, related marks, proceedings, coverage, media              |
| `signa.owners`        | Owner profiles, trademark portfolios, GLEIF corporate relationships                               |
| `signa.entities`      | Resolved entities: one company across all offices, trademarks, corporate family                   |
| `signa.attorneys`     | Attorney profiles, trademark portfolios, client lists                                             |
| `signa.firms`         | Law firm profiles, attorneys, trademark portfolios                                                |
| `signa.proceedings`   | Oppositions, cancellations, and other tribunal proceedings                                        |
| `signa.suggest`       | Cross-entity typeahead across trademarks, owners, attorneys, and firms                            |
| `signa.references`    | Classifications, offices, jurisdictions, event types, design codes, deadline and opposition rules |
| `signa.goodsServices` | Goods & services term catalog and AI-assisted specification drafting                              |
| `signa.deadlines`     | Batch maintenance-deadline computations                                                           |
| `signa.oppositions`   | Batch opposition-window computations                                                              |
| `signa.reconcile`     | Compare your own records against register data                                                    |
| `signa.portfolios`    | Portfolio CRUD, member marks, deadlines                                                           |
| `signa.savedSearches` | Saved search CRUD and re-execute                                                                  |
| `signa.watches`       | Watch CRUD, pause/resume, preview, bulk create, diagnostics                                       |
| `signa.alerts`        | Read-only alert listing, retrieval, and bulk lookup                                               |
| `signa.webhooks`      | Webhook endpoint CRUD, secret rotation, test deliveries, delivery audit                           |
| `signa.events`        | Org event stream: list and retrieve with diffs                                                    |
| `signa.organization`  | Account identity, usage, API key management, request logs                                         |

## Search and list trademarks

`search()` takes a text query with structured filters under `filters`, plus `options` for aggregations and totals:

```typescript theme={null}
const results = await signa.trademarks.search({
  query: 'SIGNA',
  strategies: ['exact', 'phonetic'],
  filters: { jurisdictions: ['US', 'EU'], nice_classes: [9, 42] },
  options: { aggregations: ['office_code'], include_total: true },
});

for (const hit of results.data) {
  console.log(hit.mark_text, hit.score);
}
console.log(results.aggregations);
console.log(results.search_meta);
```

`list()` is for filter-only or simple-query listing. Its filters are flat top-level params, not nested:

```typescript theme={null}
const page = await signa.trademarks.list({
  offices: ['uspto'],
  status_primary: 'active',
  filing_date_gte: '2024-01-01',
  sort: '-filing_date',
  limit: 50,
});
```

Both return a `SignaList`, see [Pagination](#pagination) below.

## Retrieve and batch

```typescript theme={null}
const tm = await signa.trademarks.retrieve('tm_8kLm2nPq');
console.log(tm.mark_text, tm.status.primary);

// Embed the 50 most recent prosecution events inline
const withHistory = await signa.trademarks.retrieve('tm_8kLm2nPq', { include: ['history'] });
```

Look up multiple trademarks in one call, by Signa ID or office identifier (max 100 per call):

```typescript theme={null}
const result = await signa.trademarks.batch({ ids: ['tm_8kLm2nPq', 'tm_9jNq3rTw'] });

console.log(result.data);      // trademarks that matched
console.log(result.not_found); // IDs or identifiers that didn't match
```

## Pagination

Every list and search method returns a `SignaList<T>`, which supports three consumption patterns. The first page is fetched eagerly; later pages are fetched lazily.

**Async iteration**, the simplest approach:

```typescript theme={null}
for await (const tm of signa.trademarks.list({ offices: ['uspto'] })) {
  console.log(tm.mark_text);
}
```

**Collect to an array** with `toArray()`. A safety cap of 10,000 items applies by default; pass `{ limit }` to change it:

```typescript theme={null}
const page = await signa.trademarks.list({ jurisdictions: ['US'], status_primary: 'active' });
const allMarks = await page.toArray();          // up to 10,000
const first500 = await page.toArray({ limit: 500 });
```

**Manual paging**, for full control over when the next request fires:

```typescript theme={null}
let page = await signa.trademarks.list({ offices: ['uspto'], limit: 100 });

console.log(`Page 1: ${page.data.length} items`);
console.log(`Request ID: ${page.request_id}`);

while (page.has_more) {
  page = await page.getNextPage();
  console.log(`Next page: ${page.data.length} items`);
}
```

`getNextPage()` returns an empty list (not an error) once there are no more pages. Every `SignaList` exposes `data`, `has_more`, `request_id`, and, on search responses, `search_meta` and `aggregations`. There is no public `pagination` field on the list itself, use `has_more` and `getNextPage()` to drive pagination rather than reaching for a cursor directly.

## Error handling

All errors extend `SignaError`. API errors (4xx/5xx) extend `SignaAPIError` and carry a typed subclass per status code:

```
SignaError
├── SignaAPIError
│   ├── BadRequestError        400
│   ├── AuthenticationError    401
│   ├── PermissionError        403
│   ├── NotFoundError          404
│   ├── ConflictError          409
│   ├── RateLimitError         429 (has retry_after)
│   └── InternalServerError    5xx
├── ConnectionError            DNS, TCP, TLS failures
└── TimeoutError                request exceeded timeout
```

Use `instanceof` to handle specific error types:

```typescript theme={null}
import { Signa } from '@signa-so/sdk';

const signa = new Signa({ api_key: process.env.SIGNA_API_KEY });

try {
  const tm = await signa.trademarks.retrieve('tm_invalid');
} catch (err) {
  if (err instanceof Signa.NotFoundError) {
    console.log('Trademark not found');
  } else if (err instanceof Signa.RateLimitError) {
    console.log(`Rate limited, retry after ${err.retry_after}s`);
  } else if (err instanceof Signa.AuthenticationError) {
    console.log('Invalid API key');
  } else if (err instanceof Signa.SignaAPIError) {
    console.log(`API error ${err.status}: ${err.message}`);
  } else if (err instanceof Signa.ConnectionError) {
    console.log('Network issue:', err.message);
  } else if (err instanceof Signa.TimeoutError) {
    console.log('Request timed out');
  }
}
```

Every `SignaAPIError` carries the structured error body plus the request ID:

```typescript theme={null}
try {
  await signa.trademarks.list({ offices: ['invalid_office'] });
} catch (err) {
  if (err instanceof Signa.BadRequestError) {
    console.log(err.error.type);   // machine-readable slug, e.g. "validation_error"
    console.log(err.error.detail); // human-readable detail
    console.log(err.request_id);   // include this in support requests
  }
}
```

### Automatic retries

| Error type                  | Retried?                                           |
| --------------------------- | -------------------------------------------------- |
| `ConnectionError`           | Yes                                                |
| `TimeoutError`              | Yes                                                |
| `RateLimitError` (429)      | Yes, after the `retry_after` delay                 |
| `InternalServerError` (5xx) | Yes, unless the error body says `retryable: false` |
| `BadRequestError` (400)     | No                                                 |
| `AuthenticationError` (401) | No                                                 |
| `NotFoundError` (404)       | No                                                 |

An explicit `retryable: false` in the server's error body overrides the status-based rule: a deterministic failure like the watch preview's `504` timeout is not retried, since a blind retry would re-run the same over-budget work. Retries use exponential backoff with jitter.

```typescript theme={null}
const signa = new Signa({
  api_key: process.env.SIGNA_API_KEY,
  max_retries: 3, // default: 2, set to 0 to disable
});
```

Override retry behavior per request:

```typescript theme={null}
const tm = await signa.trademarks.retrieve('tm_8kLm2nPq', undefined, { max_retries: 0 });
```

## Timeouts

The default timeout is 30 seconds. Configure it globally or per request:

```typescript theme={null}
const signa = new Signa({ api_key: process.env.SIGNA_API_KEY, timeout: 60_000 });

const tm = await signa.trademarks.retrieve('tm_8kLm2nPq', undefined, { timeout: 10_000 });
```

## Debug mode

```typescript theme={null}
const signa = new Signa({ api_key: process.env.SIGNA_API_KEY, debug: true });
```

Logs each request and response (method, URL, status, timing) to stderr.
