Skip to content
KortixKortix
Esc
↑↓navigate↵open⌘Jpreview
On this page

SDK

@kortix/sdk: one typed client for projects, sessions, connectors and the session runtime, for browsers, Node, Bun and edge runtimes.

@kortix/sdk is the TypeScript client for the Kortix API. It covers projects, sessions, connectors, secrets, accounts and the live runtime of each session. Use it to start and drive sessions from your own app, server, script or CI job.

Install

npm install @kortix/sdk
Import Use it for Runs in
@kortix/sdk The client and every framework-free module. Any runtime with a global fetch: browsers, Node 18 or later, Bun, edge workers.
@kortix/sdk/react Hooks, useSession first. Needs the optional peers react (18 or later) and @tanstack/react-query (5.75.2 or a later 5.x). React and React Native.
@kortix/sdk/server createScopedKortix, runWithKortix, createKortixAuth, createKortixAppGuard, readAppViewer, createAppViewerKortix. It imports node:async_hooks. Node and Bun. Never bundle it for a browser.

Import from the root. Two other subpaths are stable. @kortix/sdk/wire-message-id holds the wire-id clock in one module with no imports, and the root exports the same names. @kortix/sdk/workspace-search adds ranked file search over a session workspace, and the root does not export it. The other subpaths (@kortix/sdk/turns, @kortix/sdk/projects-client and similar) are deprecated aliases of root names. The full list is in Entry points and stability.

For a page with no build step, load the browser bundle. It defines window.Kortix with the same names as the root, for example Kortix.createKortix:

<script src="https://cdn.jsdelivr.net/npm/@kortix/sdk@<version>/dist/kortix.global.js"></script>

The package also ships an ES module bundle at dist/kortix.esm.min.js. Replace <version> with a published version.

A browser page calls the Kortix API directly only from an origin the API allows. Kortix Cloud allows its own origins. From your own site, send browser calls through your server, for example the same-origin proxy of Sign in with Kortix. A self-hosted instance adds origins with the API’s CORS_ALLOWED_ORIGINS setting.

Create the client

Call createKortix once per host, with the API base URL and a function that returns a token.

import { createKortix } from '@kortix/sdk';

export const kortix = createKortix({
  backendUrl: 'https://api.kortix.com/v1',
  getToken: async () => process.env.KORTIX_API_KEY ?? null,
});
Option Required What it does
backendUrl yes The API base URL, including /v1. Kortix Cloud is https://api.kortix.com/v1. A self-hosted instance is its API origin plus /v1.
getToken yes Returns the bearer token, or null when no one is signed in. The SDK calls it on every request and stores nothing. Your host owns storage and refresh.
fetch no A fetch implementation for tests, edge runtimes and adapters. Default: the global fetch.
eventStreamTransport no Delivers the live event stream’s bytes when your fetch cannot stream a response body (React Native without expo/fetch).
clientVersion no Your surface and version as <surface>/<version>, for example my-app/1.2.0. The SDK sends it as X-Kortix-Client-Version. The API writes it to the request log only.
onError no An error sink for a toast or a log. Default: no-op.
onAccessRemedy no Receives sso_required or account_mfa_required when the API refuses a request until the person signs in through single sign-on or completes multi-factor authentication. It runs for every response of the REST client, not for the session runtime or its event stream. A browser also gets the kortix:sso-required and kortix:mfa-required window events. Default: no-op.

The client holds one configuration for the whole process. A second createKortix() call with a different backendUrl or getToken re-points every earlier client and logs one console warning. A server that acts for many people at the same time uses createScopedKortix() from @kortix/sdk/server. See Authentication.

The client exposes these handles:

Handle What it reaches
kortix.projects List, provision, read and update projects.
kortix.project(projectId) Everything inside one project: sessions, secrets, connectors, apps, triggers and more, with the project id bound.
kortix.session(projectId, sessionId) One session: readiness, prompts, the event stream, messages, files and previews.
kortix.connector(slug), kortix.connectors Connector calls with a token that already carries its project, such as a session token.
kortix.auth, kortix.accounts, kortix.iam, kortix.billing, kortix.notifications Sign-in, accounts and members, access control, billing reads, and the caller’s notifications.
kortix.validateToken() Checks the token getToken returns now and gives { valid, identity?, error? }. It never throws.

The full map is in the SDK reference.

Get an API key

A script, a CI job or a server uses a personal access key (kortix_pat_, Settings → Personal access keys or kortix tokens new). Unattended automation uses a service account key (kortix_sa_, Account settings → API keys). Every credential: Choose a credential.

Kortix shows the key once. Store it as a secret and return it from getToken. Never put a personal access key in a browser bundle: a browser app signs people in with Sign in with Kortix instead.

Your first session

A session is one unit of agent work, on its own sandbox and branch. From code, it takes six steps.

Pick a project

kortix.projects.list() returns the projects the token can open. kortix.projects.provision({ name }) creates a project with a Kortix-managed repository and the starter configuration.

Create the session

kortix.project(projectId).sessions.create(input?) returns the session with its session_id. The sandbox starts to provision now, in the background. All input fields are optional: name, agent_name, model (a bare managed id such as kimi-k3, or provider/model for your own provider key; see Models), initial_prompt, labels, metadata, base_ref and more. See Create a session.

Get the session handle

kortix.session(projectId, sessionId) binds both ids. The handle has no network cost.

Wait until ready

session.ensureReady() resolves when the runtime accepts work. It returns runtimeSessionId, the id of the root conversation in the runtime. It waits up to 180 seconds by default ({ readyTimeoutMs } changes it). It throws an ApiError with code RUNTIME_UNAVAILABLE when the sandbox fails or stops, or when the time runs out. send(), stream() and messages() call it for you.

Open the stream, then send

Open session.stream({ onEvent }) before session.send(text), so no early event is lost. send() puts the prompt in the session’s inbox and resolves when the prompt is stored, not when the turn ends. The turn ends on the session.idle event whose sessionID equals runtimeSessionId.

Read the conversation

session.messages() returns { messages, hasMore }. Each message is { info, parts } in the Kortix transcript format, kortix.transcript.v1. classifyTurn(message).parts gives each part a kind you can switch on: text, reasoning, tool, file and more.

The same calls work on both session harnesses, OpenCode and pi. See Harnesses.

A complete script

This file lists your projects, creates a session, waits for one turn, and prints the transcript.

import { classifyTurn, createKortix, narrowChatEvent } from '@kortix/sdk';

const kortix = createKortix({
  backendUrl: 'https://api.kortix.com/v1',
  getToken: async () => process.env.KORTIX_API_KEY ?? null,
});

async function main() {
  // 1. Pick a project, or create one.
  const projects = await kortix.projects.list();
  const project = projects[0] ?? (await kortix.projects.provision({ name: 'my-project' }));

  // 2. Create a session. The sandbox starts to provision now.
  const { session_id } = await kortix.project(project.project_id).sessions.create({
    name: 'sdk first session',
  });
  const session = kortix.session(project.project_id, session_id);

  // 3. Wait for the runtime. runtimeSessionId names the root conversation.
  const { runtimeSessionId } = await session.ensureReady();

  // 4. Open the stream before you send, so no event is lost.
  let sent = false;
  let finish!: () => void;
  const turnDone = new Promise<void>((resolve) => (finish = resolve));
  const stream = await session.stream({
    onEvent: (raw) => {
      const event = narrowChatEvent(raw);
      if (!event || !sent) return;
      if (event.type === 'session.error' && (!event.sessionID || event.sessionID === runtimeSessionId)) {
        console.error('session error:', event.error);
        finish();
      }
      if (event.type === 'session.idle' && event.sessionID === runtimeSessionId) finish();
    },
  });

  // 5. Send the prompt and wait for the turn to end.
  sent = true;
  await session.send('List the files in this repository.');
  await turnDone;
  stream.close();

  // 6. Print the transcript.
  const { messages } = await session.messages();
  for (const message of messages) {
    for (const part of classifyTurn(message).parts) {
      if (part.kind === 'text' && !part.synthetic) console.log(`[${message.info.role}] ${part.text}`);
    }
  }
  console.log(`session: ${session_id}`);
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

Run it with tsx or Bun:

KORTIX_API_KEY=kortix_pat_... npx tsx first-session.ts
KORTIX_API_KEY=kortix_pat_... bun first-session.ts

Each run creates a new session, so ensureReady() waits while its sandbox boots. The session also appears in the web app, with the same transcript.

  • narrowChatEvent reduces the raw runtime events to the set a chat needs: message and part updates, session status, idle and error, questions, permissions and todos.
  • send(text, { model, agent }) overrides the model ({ providerID, modelID }) or the agent for one prompt. Model ids come from kortix.projects.modelPicker(projectId). Agent names come from kortix.projects.detail(projectId), in config.agents.
  • To print text while it streams, to answer questions and permission requests, or to handle errors, see Sessions from code.

Versions and stability

  • @kortix/sdk publishes to npm with each Kortix platform release and carries the release’s version number.
  • Exported names, including types, are the public contract. A renamed export keeps a @deprecated alias that works until the next major version.
  • The deprecated subpaths resolve until the next major version.
  • @kortix/sdk/internal/* serves the Kortix web app and is outside semver. It can change in any release.
  • Changes per release: the CHANGELOG.
  • Runnable examples, from one projects.list() call to a multi-tenant server: packages/sdk/examples on GitHub. The npm package does not include them.

Next steps

Was this page helpful?