# TypeScript SDK

> Call the MISSIN API from TypeScript with @missin/sdk: one typed function per public operation, secret-key auth, typed errors, timeouts and safe retries.

`@missin/sdk` is a typed client for `https://api.missin.co.uk/graphql/v1`. It has one function per public operation, grouped by toolset, and it sends only the operation's name and hash, never a query document.

## Install and make a call

1. Install the package. It needs Node 22.13 or later and is ESM only.

   ```bash
   npm install @missin/sdk
   ```

2. Create a client with a secret key and call a function.

   ```ts
   import { createMissinClient } from "@missin/sdk";

   const missin = createMissinClient({ apiKey: process.env.MISSIN_API_KEY! });
   const { allPartners } = await missin.partners.list({ first: 20 });
   ```

Create the key in Settings > Developers > Create key (https://app.missin.co.uk/settings/developers). The SDK does not read environment variables: you pass the key yourself.

## Authentication

The key must be a secret key, `sk_live_…`, and it is sent as `Authorization: Bearer`. An empty key, or a publishable `pk_` key, throws `MissinKeyError` before any request, because the API refuses publishable keys for every operation. Run the SDK on a server you control, never in a browser. See [Authentication](https://developer.missin.co.uk/get-started/authentication.md) for access levels and rate limits.

A key belongs to one workspace, and the server runs every call in it. There is no workspace argument to set, so a call cannot reach another workspace.

## Functions

Each public operation is a function on the client, under the name of its toolset. `PartnersList`, in the `partners` toolset, is `missin.partners.list`, and the same operation is `missin partners list` in the [CLI](https://developer.missin.co.uk/tools/cli.md). Every operation and its toolset is on the [API reference](https://developer.missin.co.uk/reference.md).

- `missin.partners.list({ first: 20 })` runs `PartnersList`. Page sizes and include flags you leave out are sent with the values the MISSIN app uses.
- `missin.request("PartnersList", { first: 20 })` runs any public operation by its name.
- Every function takes the operation's variables and resolves to its `data`, typed from the operation.
- The input types of every [agent tool](https://developer.missin.co.uk/mcp/tools.md) are exported too, as `ToolInputs`, so code, the [CLI](https://developer.missin.co.uk/tools/cli.md) and AI agents share one vocabulary.

A mutation that fails validation does not throw. Its result carries `userErrors`, as in the app: an array when something was refused and empty when it worked. Read it after every write. That includes a refusal that arrives the same way: some mutations answer a key that is too weak, with code `insufficient_access` among others, as a `userErrors` entry rather than an HTTP 403, and the typed functions return it as data. The [CLI](https://developer.missin.co.uk/tools/cli.md) and the MCP tools turn the same answer into a `forbidden` result (exit code 7), so check `userErrors` codes yourself when you call the SDK directly.

## Errors

Every failure the SDK raises is a `MissinError`, with the operation's name in `operation`. No error message contains your key: anything the server echoed is redacted first.

| Error | Thrown when | Useful fields |
|---|---|---|
| `MissinKeyError` | The key is empty or publishable, before any request | none |
| `MissinAuthError` | HTTP 401: the key is unknown, revoked or expired, or its creator has left the workspace | `code` |
| `MissinForbiddenError` | HTTP 403: the operation is not public, or is above the key's access level | `code` |
| `MissinRateLimitError` | HTTP 429, after any retries | `retryAfterSeconds` |
| `MissinGraphQLError` | The response carried GraphQL `errors` | `errors` |
| `MissinHttpError` | Any other non-2xx response | `status`, `code` |
| `MissinUnknownOperationError` | The name is not a public operation in this version of the SDK | none |
| `MissinTimeoutError` | A request ran past its timeout | `timeoutMs` |
| `MissinNetworkError` | The API could not be reached | `cause` |

The `code` is the one in the response, listed on [Errors](https://developer.missin.co.uk/get-started/errors.md), for example `insufficient_access` or `operation_not_public`.

```ts
import { MissinForbiddenError, MissinRateLimitError } from "@missin/sdk";

try {
  await missin.partners.list({ first: 20 });
} catch (e) {
  if (e instanceof MissinRateLimitError) console.log(`try again in ${e.retryAfterSeconds}s`);
  else if (e instanceof MissinForbiddenError) console.log(e.code);
  else throw e;
}
```

## Timeouts and retries

> **Warning:**
> A timeout does not cancel the work on the server. A write that timed out may still have happened, so list or get the record before you repeat a create.

| Option | Default | Means |
|---|---|---|
| `timeoutMs` | 30000 | The longest one attempt may take |
| `totalTimeoutMs` | 90000 | The longest one call may take across every attempt and wait. No attempt starts, and no retry is slept on, past it |
| `maxRetries` | 2 | Extra attempts for a query after a 429 or a network error, timeouts included |

Only queries are retried. A mutation is sent exactly once. A 429 is waited out only when its `Retry-After` is 30 seconds or less and fits inside `totalTimeoutMs`; a longer wait comes back to you as `MissinRateLimitError`.

You can also pass `baseUrl` and a `fetch` implementation to `createMissinClient`.

## API versions

Each SDK release is generated from one API version and sends it as `Missin-Version` on every request, so code written against it keeps receiving the same shapes after the API moves on. `API_VERSION` says which:

```ts
import { API_VERSION } from "@missin/sdk";
```

`createMissinClient({ apiKey, apiVersion })` sends another version instead. The types still describe the version the SDK was generated from, so only do this to try a newer version before upgrading. `apiVersion: "latest"` follows the current version. [Versions and stability](https://developer.missin.co.uk/guides/stability.md) says how long an older version keeps working.

## Versions

The SDK and the CLI are versioned together. A public operation that is renamed or removed is listed in the [changelog](https://developer.missin.co.uk/changelog.md) before it stops working.

Source: https://developer.missin.co.uk/tools/sdk
