Getting started
Install @kortix/sdk, create a client, authenticate, and make your first calls.
Install
pnpm add @kortix/sdkreact (≥18) and @tanstack/react-query (≥5) are optional peers — needed only
if you use the @kortix/sdk/react hooks. The core client is framework-agnostic
and works in Node, the browser, and React Native.
Create a client
createKortix(config) wires the platform seam once and returns one client. The
only required config is backendUrl and a getToken that returns your Kortix
token.
import { createKortix } from '@kortix/sdk';
export const kortix = createKortix({
backendUrl: 'https://api.kortix.com/v1',
getToken: async () => process.env.KORTIX_API_KEY!, // your Kortix API key
});Config
| field | type | required | what |
|---|---|---|---|
backendUrl | string | yes | Kortix API base, e.g. https://api.kortix.com/v1 |
getToken | () => Promise<string | null> | yes | Returns the bearer token — a Kortix API key, a PAT (kortix_pat_…), or a Supabase JWT |
getUserId | () => Promise<string | null> | no | Current user id, when the host knows it |
billingEnabled | boolean | no | Toggles billing-gated behaviour |
onError · onToast · onNotify | callbacks | no | Host sinks for errors, toasts, and notifications |
sandboxId | string | null | no | Default sandbox id for local/single-sandbox hosts |
featureFlags | object | no | Per-flag overrides for @kortix/sdk/feature-flags — see Modules |
createKortix calls configureKortix for you — the one global seam every SDK
module reads. Call it once at app startup.
Authentication
The SDK sends Authorization: Bearer <token> on every request; getToken supplies
it. The default is an API key — create one in Settings → API keys
(account-wide, or scoped to a single project), store it as a secret, and return it:
const kortix = createKortix({
backendUrl: 'https://api.kortix.com/v1',
getToken: async () => process.env.KORTIX_API_KEY!,
});The key acts only on that project (rejected 403 elsewhere). Building a web app on
Kortix's own login instead? Return the user's Supabase session token. Full detail —
scopes, rotation, web vs backend — is on the Authentication
page.
Keys never live inside the SDK — getToken is called on demand, and the host owns
storage, caching, and refresh.
Your first calls
// list your projects
const projects = await kortix.projects.list();
// open a project, read its detail
const detail = await kortix.project(projectId).detail();
// create + start a session (one agent run, in its own sandbox on its own branch)
const created = await kortix.project(projectId).sessions.create();
const s = kortix.session(projectId, created.session_id);
await s.start();
// talk to the running agent — the typed opencode runtime, reached via the SDK
await s.runtime.session.prompt({
sessionID: (await s.ensureReady()).opencodeSessionId,
parts: [{ type: 'text', text: 'Add a README' }],
});Don't import @opencode-ai/sdk, backendApi, or authenticatedFetch in app
code. Everything is reachable through createKortix or a @kortix/sdk/* subpath.
In a React app
For a live UI, don't drive the runtime by hand — one hook runs a session:
import { useSession } from '@kortix/sdk/react';
function Chat({ projectId, sessionId }: { projectId: string; sessionId: string }) {
const s = useSession(projectId, sessionId);
if (s.phase !== 'ready') return <Booting stage={s.stage} onRetry={s.retry} />;
return (
<>
{s.messages.map(({ info, parts }) => <Message key={info.id} info={info} parts={parts} />)}
<Composer busy={s.isBusy} onSend={(t) => s.send(t)} onStop={s.cancel} />
</>
);
}useSession owns /start, the runtime switch, the SSE stream, readiness, and the
canonical id — no provider to mount, no poller, no server store. See
React hooks.
Next
- The client — the full facade surface.
- Sessions — lifecycle, runtime health, previews, and the opencode runtime.
- React hooks — live, reactive data for your UI.