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

# TypeScript SDK reference

> Every public class, method, and error of the Kardinal TypeScript SDK (@kardinal-ai/sdk): signatures, the API endpoint behind each call, and what each returns.

Reference for `@kardinal-ai/sdk` 1.0.1, for Node.js 22 or later. Every method below returns a `Promise`, except `plans.iterAll`, an async iterator. Field-level documentation for plans and solutions lives in the [API Reference](/api-reference/plan/create-a-plan).

## Client

### `Kardinal`

```ts theme={null}
new Kardinal(options?: KardinalOptions)
```

| Option | Type | Default | Description |
| - | - | - | - |
| `apiKey` | `string` | `KARDINAL_API_KEY` | Your API key. Throws `MissingApiKeyError` if none is found, `EmptyApiKeyError` for `""`. |
| `baseUrl` | `string` | `KARDINAL_BASE_URL`, then `https://app.kardinal.ai/api/v2` | API host. Leave unset. |
| `timeoutMs` | `number` | `30000` | Milliseconds, one deadline for the whole request, reading the response included. |
| `maxRetries` | `number` | `3` | Retries of a read, `update`, `delete`, `start`, or `stop` after a network error, a `429`, or a `5xx`. |
| `onWarning` | `(message: string, fields: UnknownField[]) => void` | `console.warn` | Receives the likely typos `update` can only warn about. |

The methods hang off `k.routing.plans` and `k.routing.solutions`. There's nothing to close. See [Client configuration](/sdks/configuration).

## Plans

### `plans.submit`

```ts theme={null}
k.routing.plans.submit(plan: PlanCreateInput, opts?: { allowUnknown?: string[] }): Promise<Plan>
```

[`POST /plans`](/api-reference/plan/create-a-plan). Validates the plan locally, then creates it and starts its optimization. Returns the stored `Plan`, with the `id` the server assigned and `warnings`. Throws a `ZodError` for an invalid plan, and `UnknownFieldError` for a key no schema declares unless named in `allowUnknown`, both before sending. Consumes credits with a production key. **Never retried.**

### `plans.get`

```ts theme={null}
k.routing.plans.get(planId: string): Promise<Plan>
```

[`GET /plans/{planId}`](/api-reference/plan/retrieve-a-plan). Returns the plan as currently stored. Rejects with `KardinalNotFoundError` if it doesn't exist.

### `plans.update`

```ts theme={null}
k.routing.plans.update(plan: Plan, opts?: { force?: boolean; allowUnknown?: string[] }): Promise<Plan>
```

[`PUT /plans/{planId}`](/api-reference/plan/update-a-plan). Replaces the whole plan: anything left out is removed. The version increments, and the plan is re-optimized, only if the content changed; an identical update is a free no-op. Fields unknown to this SDK version that `get` read are sent back untouched; a key you added that no schema declares throws `UnknownFieldError`, unless named in `allowUnknown`. On a plan this client didn't read, a likely typo only goes to `onWarning`. `force: true` unarchives an archived plan first. Retried.

### `plans.delete`

```ts theme={null}
k.routing.plans.delete(planId: string): Promise<void>
```

[`DELETE /plans/{planId}`](/api-reference/plan/delete-a-plan). Permanently deletes the plan and its solution, and stops any optimization in progress. Retried.

### `plans.list`

```ts theme={null}
k.routing.plans.list(opts?: { page?: number; itemsPerPage?: number; archived?: "excluded" | "included" | "only" }): Promise<Page<PlanLight>>
```

[`GET /plans`](/api-reference/plan/retrieve-the-list-of-plans-in-a-light-representation). Returns one page of plans, in their light form; defaults: page `1`, `20` items, archived plans excluded. A page past the last one returns the last page.

### `plans.iterAll`

```ts theme={null}
k.routing.plans.iterAll(opts?: { archived?: "excluded" | "included" | "only" }): AsyncGenerator<PlanLight>
```

`GET /plans`, every page. Iterates over every plan with `for await`, fetching pages as it goes.

### `plans.start`

```ts theme={null}
k.routing.plans.start(planId: string, opts?: { force?: boolean }): Promise<PlanReference>
```

[`PUT /plans/{planId}/running`](/api-reference/plan/stop-or-restart-the-optimization-of-a-plan). Restarts the optimization of the current version, without changing its content or version. Returns `agencyId`, `planId`, `planVersion`, and `warnings`. Retried.

### `plans.stop`

```ts theme={null}
k.routing.plans.stop(planId: string, opts?: { force?: boolean }): Promise<PlanReference>
```

[`PUT /plans/{planId}/running`](/api-reference/plan/stop-or-restart-the-optimization-of-a-plan). Stops the optimization in progress, without changing the plan's content or version. Retried.

### `plans.getStatus`

```ts theme={null}
k.routing.plans.getStatus(planId: string): Promise<PlanState | string>
```

[`GET /plans/{planId}/state`](/api-reference/plan/retrieve-the-latest-state-of-a-plan). Returns the plan's current state, a single value: `waiting`, `processing`, `optimizing`, `optimized`, `stopped`, `interrupted`, and so on. A state this SDK version doesn't know comes back as its raw string. Not the detailed `PlanStatus` object found in `Plan.status`.

## Solutions

### `solutions.wait`

```ts theme={null}
k.routing.solutions.wait(planId: string, timeoutMs?: number): Promise<WaitedSolution>
```

`GET /plans/{planId}/state`, then [`/solution`](/api-reference/solution/retrieve-a-plan-solution). Resolves once the optimization of the current version is over, with its solution. Reads the state every 10 seconds at first, up to every 60 seconds. `solution.finalState` tells how it ended: `optimized`, `interrupted`, or `stopped`. Rejects with `KardinalOptimizationError` (`NO_SOLUTION`, `PLAN_SUPERSEDED`, `OPTIMIZATION_STALLED`), `KardinalNotFoundError` if the plan is deleted meanwhile, and `KardinalTimeoutError` once `timeoutMs` milliseconds have elapsed. See [Waiting for the solution](/sdks/plan-lifecycle#waiting-for-the-solution).

### `solutions.get`

```ts theme={null}
k.routing.solutions.get(planId: string): Promise<Solution>
```

[`GET /plans/{planId}/solution`](/api-reference/solution/retrieve-a-plan-solution). Returns the current best solution right away, without waiting. Rejects with `KardinalNotFoundError` until the engine has a first solution.

### `solutions.getObjectives`

```ts theme={null}
k.routing.solutions.getObjectives(planId: string): Promise<SolutionObjectives>
```

[`GET /plans/{planId}/solution/objectives`](/api-reference/solution/retrieve-the-objectives-of-a-plan-solution). Returns the value of each objective, without the tours: a plain array of `SolutionObjective`, with a `warnings` property.

## Examples

### `examples.smallDeliveryPlan`

```ts theme={null}
import { examples } from "@kardinal-ai/sdk";

examples.smallDeliveryPlan(): PlanCreate
```

A ready-to-submit plan: two resources, eight single-stop orders around central Paris, and a 10-second optimization. The working window starts tomorrow at 8:00. Each call returns a new object.

## Local checks

### `unknownFields`

```ts theme={null}
import { unknownFields } from "@kardinal-ai/sdk";

unknownFields(plan: unknown): UnknownField[]
```

Runs the check `submit` makes, without sending anything: every key no schema declares, with its path and, when one is close, the real field name it was likely meant to be. Empty when the plan is clean.

`parsePlanCreate(data, allowUnknown?)` runs all of `submit`'s local checks, `zod` validation included, and returns the plan with its defaults applied; `parsePlan(data)` validates a stored plan, such as one read from a file before an `update`.

## Types

### `PlanCreate`

The plan you submit: no `id`, the server assigns it. `PlanCreateInput` is the same shape before defaults are applied: the type to use for a plan you build by hand.

### `Plan`

The plan as stored, returned by `submit`, `get`, and `update`: the same content plus `id`, `version`, `status`, `state`, and `agencyId`, and `warnings` from the call that returned it.

### `Page`

Returned by `plans.list`: `items`, `page`, `totalPages`, `total`, `nextPage` (`null` on the last page), `previousPage` (`null` on the first page), and `itemsPerPage`.

### Model types

The package exports a type for every schema of the [API specification](/api-reference/plan/create-a-plan) (`Resource`, `SingleStop`, `AlternativesStop`, `Position`, `TimeWindow`, `Solution`, `Tour`, `Warning`, and so on), with the API's field names and descriptions. Import them with `import type`.

## Exceptions

All importable from `@kardinal-ai/sdk`, all subclasses of `KardinalError`. See [Errors, retries, and warnings](/sdks/errors-and-warnings).

| Error | Thrown on |
| - | - |
| `KardinalAuthError` | `401` |
| `KardinalLimitError` | Any `403`: plan-size limit, quota, or credits |
| `KardinalValidationError` | `400` |
| `KardinalNotFoundError` | `404` |
| `KardinalRateLimitError` | `429` |
| `KardinalServerError` | `5xx` |
| `KardinalConnectionError` | No HTTP response |
| `KardinalTimeoutError` | `solutions.wait` time limit elapsed |
| `KardinalOptimizationError` | `solutions.wait` has no solution to return |
| `UnknownFieldError` | Unknown key found before sending |
| `MissingApiKeyError`, `EmptyApiKeyError` | No usable key when creating the client |

A plan that fails local validation throws a `ZodError`, from `zod`, instead.

Every error carries `.code`, `.status`, `.errors`, `.details`, `.retryable`, `.warnings`, and `.requestId`.

## Known limitations

* Node.js only: browsers aren't supported.
* The inside of the rarer nested objects (`cost`, `breaks`, `objectives`, constraints) isn't validated locally, and a misspelled key there isn't reported.
* `update`'s exact check of unknown keys needs the plan to have been read by the same client, in the same process; it remembers the last 64 plans.
* `.requestId` is usually `null`: the API sends an `X-Request-Id` header only on some responses.
* The `kardinal` command needs `commander` installed alongside the package.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.