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

# SDK Reference

> Every SDK method with TypeScript signatures, example responses, and the Python equivalents.

Seyn ships two official SDKs wrapping the v1 API in typed methods: **TypeScript** (private beta) and **Python** (early alpha). This page documents the TypeScript surface in full; the Python SDK mirrors it one-to-one, so the same reference applies.

## Installation

See the [Quickstart](/quickstart#2-install-the-sdk) for install paths. Neither SDK is on a public registry yet: contact [support@seynlabs.com](mailto:support@seynlabs.com) for access until `npm install @seyn/sdk` and `pip install seyn-sdk` go live.

## Python SDK (early alpha)

The Python SDK exposes the same four resources with the same semantics, in Python idiom: snake\_case arguments and fields (`top_k`, `review_status`), typed result objects, and `SeynError` carrying the same error codes.

```python theme={null}
import os
from seyn import SeynClient

seyn = SeynClient(api_key=os.environ["SEYN_API_KEY"])

result = seyn.knowledge.query(q="When does a loan need senior approval?", top_k=5)
for hit in result.results:
    print(f"[{hit.score:.2f}] {hit.description}")

trail = seyn.rules.provenance(result.results[0].rule_id)
print(f"{len(trail.source_events)} events, {len(trail.raw_records)} raw records")
```

<Note>
  **Early alpha** means the method surface matches this reference, but Python-side ergonomics (async client, retries, pagination helpers) are still moving. Pin the exact version you receive and read the changelog that ships with each build.
</Note>

Everything below is the canonical reference, shown in TypeScript. Translate names mechanically: `topK` becomes `top_k`, `reviewStatus` becomes `review_status`, and methods are synchronous in Python.

## Client

### `new SeynClient(options)`

```ts theme={null}
import { SeynClient } from "@seyn/sdk";

const client = new SeynClient({
  apiKey: process.env.SEYN_API_KEY!,
  // baseUrl: "https://api-dev.seynlabs.com",  // optional, defaults to api.seynlabs.com
});
```

| Option    | Type     | Default                    | Notes                                           |
| --------- | -------- | -------------------------- | ----------------------------------------------- |
| `apiKey`  | `string` | (none)                     | Required. `sk_live_*` token from the dashboard. |
| `baseUrl` | `string` | `https://api.seynlabs.com` | Override for staging or local development.      |

Throws synchronously if `apiKey` is empty or whitespace.

### Resources

The client exposes four resource objects:

* [`client.knowledge`](#knowledge): natural-language search
* [`client.rules`](#rules): process rule operations
* [`client.libraries`](#libraries): knowledge libraries
* [`client.patterns`](#patterns): aggregate analytics

Every method accepts an optional `signal?: AbortSignal` for cancellation/timeouts.

## Knowledge

### `client.knowledge.query(options)` <a id="knowledge-query" />

`GET /v1/knowledge/query`: runs a natural-language search.

```ts theme={null}
const result = await client.knowledge.query({
  q: "When does a loan need senior approval?",
  topK: 5,
  strategy: "hybrid",          // optional: "auto" | "structured" | "hybrid" | "graph"
  libraryId: "uuid-here",       // optional: limit to one library
  includeExplain: true,         // optional: include the retrieval explain blob
});

// result.results is V1KnowledgeQueryHit[]
for (const hit of result.results) {
  console.log(`[${hit.score.toFixed(2)}] ${hit.description}`);
}
```

**Returns** `Promise<V1KnowledgeQueryResult>`:

```ts theme={null}
{
  results: Array<{
    ruleId: string;
    description: string;
    processId: string | null;
    processName: string | null;
    step: string | null;
    confidence: number;
    score: number;
  }>;
  explain?: V1RetrievalExplain;  // present only if includeExplain was true
}
```

### `client.knowledge.memory(options)` <a id="knowledge-memory" />

`POST /v1/knowledge/memory`: teach Seyn something in plain language. Send a sentence describing what you want the knowledge base to know; Seyn extracts the claims, reconciles them against what it already believes, and writes them as assertions. Processing is async, attributed as a human edit. Needs an `ingest`-scoped key.

```ts theme={null}
const memory = await client.knowledge.memory({
  text: "Deals over $50M always need board sign-off, not just the CEO.",
  // libraryId: "uuid-here",   // optional: defaults to the active library
});

// poll until applied
const status = await client.knowledge.memoryStatus(memory.memoryId);
if (status.status === "applied") {
  console.log(status.summary);
  console.log(`${status.assertionsCreated} created, ${status.assertionsSuperseded} superseded`);
}
```

**Returns** `Promise<V1MemorySubmission>` with `memoryId` and `status`. Throws `SeynError` with `code: "INSUFFICIENT_SCOPE"` for a read-only key, or `"LIBRARY_NOT_FOUND"` for an unknown `libraryId`.

## Rules

### `client.rules.list(options?)` <a id="rules-list" />

`GET /v1/rules`: paginated list of rules in the org.

```ts theme={null}
const rules = await client.rules.list({
  reviewStatus: "confirmed",   // "inferred" | "confirmed" | "modified" | "rejected"
  libraryId: "uuid-here",
  search: "approval",
  limit: 50,
  offset: 0,
});
```

**Returns** `Promise<V1RuleSummary[]>`.

### `client.rules.get(id)` <a id="rules-get" />

`GET /v1/rules/:id`: full detail for a single rule.

```ts theme={null}
const rule = await client.rules.get("7f3cb37f-7d67-43f1-a3eb-a95ffdafb6b9");
console.log(rule.condition);   // structured logic tree
console.log(rule.evidence);    // supporting evidence
```

**Returns** `Promise<V1RuleDetail>`. Throws `SeynError` with `code: "NOT_FOUND"` if the rule doesn't exist in the caller's org.

### `client.rules.provenance(id)` <a id="rules-provenance" />

`GET /v1/rules/:id/provenance`: the full audit chain back to source records.

```ts theme={null}
const trail = await client.rules.provenance(rule.id);

console.log(trail.rule.description);
console.log(`Produced by ${trail.inferenceLog?.model}`);
console.log(`From ${trail.sourceEvents.length} events,`);
console.log(`across ${trail.rawRecords.length} raw records`);
console.log(`ingested via ${trail.rawRecords[0]?.connectorType}`);
```

**Returns** `Promise<V1Provenance>`. Throws `SeynError` with `code: "NOT_FOUND"` if the rule doesn't exist.

<Tip>
  Provenance responses can be large (KBs to tens of KBs). Each `sourceEvents` or `rawRecords` array can contain dozens of entries: that's the point. If you only need a summary, just count the arrays.
</Tip>

## Libraries

### `client.libraries.list(options?)` <a id="libraries-list" />

`GET /v1/libraries`: list versioned knowledge libraries.

```ts theme={null}
const libraries = await client.libraries.list({
  status: "active",   // "draft" | "active" | "archived"
  limit: 20,
});
```

**Returns** `Promise<V1LibrarySummary[]>`.

### `client.libraries.get(id)` <a id="libraries-get" />

`GET /v1/libraries/:id`: single library detail.

```ts theme={null}
const lib = await client.libraries.get("uuid-here");
console.log(`Library v${lib.version}, status=${lib.status}`);
```

**Returns** `Promise<V1LibraryDetail>`. Throws `SeynError` with `code: "LIBRARY_NOT_FOUND"` if missing.

### `client.libraries.rules(libraryId, options?)` <a id="libraries-rules" />

`GET /v1/libraries/:id/rules`: rules scoped to one library.

```ts theme={null}
const rules = await client.libraries.rules("library-uuid", {
  reviewStatus: "confirmed",
  limit: 100,
});
```

**Returns** `Promise<V1RuleSummary[]>`. Throws `SeynError` with `code: "LIBRARY_NOT_FOUND"` if the library doesn't exist.

## Patterns

### `client.patterns.metrics()` <a id="patterns-metrics" />

`GET /v1/patterns/metrics`: aggregate pipeline analytics.

```ts theme={null}
const m = await client.patterns.metrics();

console.log(`${m.totalDeals} total deals, ${m.totalDeadDeals} dead`);
console.log("Median days in each stage:");
for (const row of m.stageVelocity) {
  console.log(`  ${row.stage}: ${row.medianDays}d (p90 ${row.p90Days}d)`);
}
```

**Returns** `Promise<V1PatternMetrics>`. See [Core Concepts → Pattern Metrics](/core-concepts#pattern-metrics) for what each array means.

## Ingestion

Reading is the bulk of the v1 surface, but you can also push data in. The ingestion methods need an API key with the `ingest` scope (request one from support); read-only keys get `INSUFFICIENT_SCOPE`. See [Connectors → Build your own](/platform/connectors#build-your-own) for the model.

### `client.sources.create(options)` <a id="sources-create" />

`POST /v1/sources`: register a custom source to ingest from any system.

```ts theme={null}
const source = await client.sources.create({
  name: "Internal CRM",
  entityTypes: ["deal", "account", "activity"],
});
```

**Returns** `Promise<V1Source>`. The `source.id` is what you ingest into.

### `client.ingest(options)` <a id="ingest" />

`POST /v1/ingest`: push a batch of records. Records are deduplicated by content hash and normalized asynchronously; reuse a `recordId` on update and Seyn supersedes the prior version. Up to 500 records per call.

```ts theme={null}
const batch = await client.ingest({
  sourceId: source.id,
  records: [
    {
      recordId: "deal-4471",
      entityType: "deal",
      sourceUpdatedAt: "2026-06-10T14:03:00Z",
      payload: {
        name: "Acme Corp renewal",
        stage: "negotiation",
        amount: 42000,
        owner: "jane.smith@acme.com",
        movedToStageBy: "jane.smith@acme.com",
      },
    },
  ],
});

console.log(`${batch.accepted} accepted, ${batch.deduplicated} deduped`);
```

**Returns** `Promise<V1IngestBatch>` with `batchId`, `accepted`, `deduplicated`, and `rejected[]`. Throws `SeynError` with `code: "SOURCE_NOT_FOUND"` for an unknown source, or `"PAYLOAD_TOO_LARGE"` past the batch limits.

### `client.ingest.status(batchId)` <a id="ingest-status" />

`GET /v1/ingest/{batchId}`: poll a batch. Normalization is async, so a batch moves `queued` → `processing` → `done`.

```ts theme={null}
const status = await client.ingest.status(batch.batchId);
if (status.status === "done") {
  console.log(`${status.recordsCreated} records, ${status.eventsCreated} events`);
}
```

**Returns** `Promise<V1IngestBatch>`.

## Errors

Every non-2xx response throws `SeynError`:

```ts theme={null}
import { SeynClient, SeynError } from "@seyn/sdk";

try {
  await client.rules.get("missing-id");
} catch (err) {
  if (err instanceof SeynError) {
    if (err.code === "NOT_FOUND") {
      return null;
    }
    if (err.code === "RATE_LIMITED") {
      // Back off for 60s before retrying. Replace with your own retry/backoff strategy.
      await new Promise((resolve) => setTimeout(resolve, 60_000));
      // retry...
    }
    if (err.code === "KEY_REVOKED") {
      // Wire this up to PagerDuty / Slack / whatever your team uses.
      console.error("Seyn key was revoked: rotate ASAP");
    }
    console.error(err.code, err.statusCode, err.message);
  }
  throw err;
}
```

`SeynError` shape:

```ts theme={null}
class SeynError extends Error {
  code: V1ErrorCode | "UNKNOWN_ERROR";
  statusCode: number;
  requestId: string | undefined;  // include in support tickets
  message: string;
}
```

See [Authentication → Error codes](/authentication#error-codes) for the full table.

## Cancellation and timeouts

Pass an `AbortSignal` on any method to cancel in-flight requests:

```ts theme={null}
const ac = new AbortController();
setTimeout(() => ac.abort(), 5_000);

try {
  const rules = await client.rules.list({ signal: ac.signal });
} catch (err) {
  if (err.name === "AbortError") {
    console.log("query took too long, gave up");
  }
}
```

For per-call timeouts on Node 20+:

```ts theme={null}
await client.rules.list({ signal: AbortSignal.timeout(5_000) });
```

The SDK does not provide built-in timeouts or retries: bring your own.

## What's not in the SDK

* Knowledge writes: you can ingest source data, but rules and libraries are produced by extraction, not written directly.
* Auto-pagination helpers: use `limit`/`offset` directly.
* Server-sent events / streaming: not part of v1.
* Browser bundle: Node-first, and ingest-scoped keys must stay server-side. For browser use, proxy through your own backend.
