# TypeScript SDK

> Typed clients for the Management API, Content Delivery API and Customer API, plus helpers for webhooks, previews, SEO files and the edge.

> Markdown version of https://kanorio.com/en/developers/sdk. Append `.md` to any developer docs URL for this format. Index: https://kanorio.com/llms.txt · MCP: https://app.kanorio.com/api/mcp

`@kanorio/sdk` is a thin, fully typed client generated from the three OpenAPI documents. Paths, parameters, request bodies and responses are checked at compile time. It runs anywhere `fetch` does: Node.js, edge runtimes, Deno, Bun and browsers.

```bash
npm install @kanorio/sdk
```

## Clients

| Function | API | Credential |
|---|---|---|
| `createManagementClient({ token })` | Management API at `https://app.kanorio.com/api/v1` | `knr_pat_` or `knr_sa_`. Server only. |
| `createDeliveryClient({ siteUrl, previewToken? })` | Content Delivery API of one site | None, or `knr_pv_` for drafts |
| `createCustomerClient({ siteUrl, token })` | Customer API of one site | `knr_cat_` |

Each client has `get`, `post`, `put`, `patch` and `delete`. Path parameters go in `params`, query parameters in `query` and the JSON body in `body`.

```ts
import { createManagementClient } from "@kanorio/sdk";

const kanorio = createManagementClient({ token: process.env.KANORIO_TOKEN! });

const page = await kanorio.get("/sites/{id}/pages/{pageId}", {
  params: { id: "ws_example123", pageId: "pg_example456" },
});
```

`token` can be a function, so you can fetch a rotated secret on each request.

## Errors

Non-`2xx` responses throw `KanorioApiError` with `code`, `status`, `message` and `details`. `retryAfter` is filled from the `Retry-After` header or `details.retryAfter`.

```ts
import { KanorioApiError } from "@kanorio/sdk";

try {
  await kanorio.post("/sites/{id}/posts/{postId}/publish", { params: { id, postId }, body: {} });
} catch (error) {
  if (error instanceof KanorioApiError && error.code === "SITE_NOT_PUBLISHED") {
    await kanorio.post("/sites/{id}/publish", { params: { id }, body: {} });
  } else {
    throw error;
  }
}
```

## Pagination

`paginate` turns any list endpoint into an async iterator that follows `nextCursor`. `listAll` collects every page into an array.

```ts
import { createDeliveryClient, paginate } from "@kanorio/sdk";

const delivery = createDeliveryClient({ siteUrl: "https://s-xxxxxxxxxxxx.kanorio.com" });

for await (const item of paginate((cursor) =>
  delivery.get("/collections/{key}/items", { params: { key: "projects" }, query: { cursor, limit: 100 } })
)) {
  console.log(item.slug);
}
```

## Helpers

| Import | What it does |
|---|---|
| `verifyWebhookSignature`, `createRevalidateHandler` | Verify `X-Kanorio-Signature` and revalidate `data.cacheTags` (prefixed with `kanorio:`). See [Webhooks](https://kanorio.com/en/developers/webhooks). |
| `cacheTag`, `localizeCacheTags`, `prefixCacheTags` | Build the cache tags a fetch should carry (`cacheTag.collection("blog")`, `cacheTag.item(id)`, `localizeCacheTags(tags, "fr-FR")`), with the same `kanorio:` prefix the webhook handler revalidates |
| `seoRewrites({ siteUrl, framework })` | Serve the site's sitemap, robots.txt, llms.txt and feeds from your own domain |
| `fetchDeliveryJsonLd({ siteUrl, path })` | Schema.org JSON-LD for a page |
| `@kanorio/sdk/edge` › `createKanorioMiddleware` | Apply redirects (exact, then `/*` wildcard), block AI crawlers the owner blocked, serve the IndexNow key file and, on multilingual sites, the locale rules: redirect the primary locale's prefix to the root, serve `/{prefix}/llms.txt` and `/{prefix}/rss.xml`, match redirects without the prefix, and report the request's locale in `evaluate()` |
| `requestLocaleOf`, `localizedPath`, `languageSwitcher` | Resolve the locale from a pathname with the site's `locales[]` (`GET /site`), build the same path in another locale, list the language menu |
| `buildAlternates` | Canonical and `hreflang` for one page from `resolvedLocale` / `availableLocales`, with the hosted site's rules (self-canonical per locale, `x-default` to the primary locale, fallback pages canonical to the primary) |
| `@kanorio/sdk/preview` › `createPreviewRouteHandler` | Turn the dashboard's preview link into a draft-mode session |
| `@kanorio/sdk/forms` › `forms.client` | Fetch a form, its bot-protection challenge and upload tickets, then submit |
| `parseDeliveryCacheTags`, `frameworkCacheTags` | Read `X-Kanorio-Cache-Tags` from a Content Delivery API response, raw or prefixed for your framework's cache |

## Types for your content model

Collections are defined per site, so their field types come from the site itself. Generate them with the [CLI](https://kanorio.com/en/developers/cli):

```bash
npx @kanorio/cli types --site https://s-xxxxxxxxxxxx.kanorio.com --out src/kanorio.d.ts
```

## Versioning

The SDK follows semantic versioning. Its version is sent with each request, so Kanorio can warn you about deprecated operations you still call. See [Versioning](https://kanorio.com/en/developers/basics/versioning).
