SDK reference
Every @kortix/sdk entry point, client member, project and session handle method, and framework-free module, grouped by domain.
This page lists the public surface of @kortix/sdk: each entry point, each member of the client, each method of the project and session handles, and the framework-free modules. It is for developers who already have a client and need the exact method name and arguments. The client table gives the route prefix of each member. To install the package and run a first session, start at SDK.
import { createKortix } from '@kortix/sdk';
const kortix = createKortix({ backendUrl: 'https://api.kortix.com/v1', getToken });
const p = kortix.project(projectId); // every project resource, id bound
const s = kortix.session(projectId, sessionId); // one session, both ids bound
await p.secrets.list();
await s.send('Run the tests.');
Each client method calls the Kortix API. Session runtime methods reach the session’s sandbox through the API at {backendUrl}/p/…. Route paths on this page are relative to backendUrl, which ends in /v1. Each route is in the REST reference. A failed call throws an ApiError with status, code and details.
Entry points and stability
| Import | Status | What it holds |
|---|---|---|
@kortix/sdk |
Stable | The client and every framework-free module. It never imports React or node:*. It runs in browsers, Node 18 or later, Bun and edge runtimes. |
@kortix/sdk/react |
Stable | The React hooks. It is the only entry that imports React. See React. |
@kortix/sdk/server |
Stable | Server helpers for Node and Bun (table below). It imports node:async_hooks. Never bundle it for a browser. |
@kortix/sdk/wire-message-id |
Stable | mintWireMessageId and WIRE_MESSAGE_ID in one module with no imports. The root exports the same names. |
@kortix/sdk/workspace-search |
Stable | createWorkspaceSearchClient(fileClient): ranked file and folder search over a session workspace, with a cached index. fileClient has listFiles(path) and findFiles(query, options?). |
@kortix/sdk/message-queue |
Deprecated | Client-side prompt queue reducers. Kortix orders prompts on the server: use s.send or useSession().send. |
@kortix/sdk/turns, /files, /session, /session/url, /projects-client, /platform-client, /api-client, /auth, /config, /event-stream, /opencode-client, /opencode-errors, /feature-flags, /fresh-sessions, /instance-routes |
Deprecated | Aliases. Each re-exports names that the root already exports. Import from the root. |
@kortix/sdk/sync-store, /server-store, /sandbox-connection-store, /opencode-pending-store, /idb-sync-cache |
Deprecated | Aliases of the internal web-app stores. They have no replacement. Read session state with useSession from @kortix/sdk/react. |
@kortix/sdk/internal/* |
Internal | State stores for the Kortix web app. Outside semver. Do not import. |
Deprecated subpaths and names resolve until the next major version. The package also ships two browser bundles, dist/kortix.esm.min.js and dist/kortix.global.js (window.Kortix). See Install.
@kortix/sdk/server
| Export | What it does |
|---|---|
createScopedKortix(config) |
The same client as createKortix, but every call, including calls on project() and session() handles, runs with this config only. Create one per request in a server that acts for many people. |
runWithKortix(config, fn) · getScopedConfig() |
Run fn with a request-scoped config (Node AsyncLocalStorage) · read the config in scope. |
forwardKortixRequest(options) |
Forward one same-origin request to the Kortix API with an upstream token. It strips host credentials and keeps a streaming response body. |
createKortixAuth(options) |
Sign in with Kortix for your own app: OAuth 2.1 with PKCE, an encrypted session cookie, refresh, sign-out, /me and a same-origin /proxy. Register the client with kortix.iam.oauthClients.create. See Sign in with Kortix. |
readAppViewer(request, options?) |
Inside a Kortix App: verify the signed x-kortix-app-viewer header with KORTIX_APP_VIEWER_SECRET and return the viewer, or null. |
createAppViewerKortix(request, options) |
Inside a Kortix App: a client that calls the Kortix API as the viewer. |
createKortixAppGuard(options) |
One viewer resolver for an App behind the Kortix gate or set to public. requireViewer and requireGroup return a viewer or a Response. |
KortixAuthError, AppViewerUnavailableError, safeReturnTo, APP_VIEWER_HEADER, APP_VIEWER_TOKEN_HEADER, APP_VIEWER_SECRET_ENV, KORTIX_SESSION_SENTINEL |
Errors, a return-URL guard and the header and environment variable names. |
App viewers are described in Know who is looking.
The client
createKortix(config) returns one client. A second call with a different backendUrl or getToken re-points every earlier client and logs one warning. The options are in Create the client.
| Member | What it reaches | Routes |
|---|---|---|
config |
The config in effect. | none |
auth |
Headless sign-up, sign-in, magic link, OTP, OAuth, SSO, MFA, refresh, sign-out. | /auth/… |
accounts |
Accounts, members, invites, API keys, connected apps, account secrets, branding, the audit log. | /accounts/… |
iam |
Assignments, roles, permissions, groups, agent identities, OAuth clients, permission probes. | /accounts/{accountId}/iam/… |
accountInvites |
The invitee’s side of an account invite. | /account-invites/… |
projects |
Project list, create, provision and model access. | /projects/… |
project(projectId) |
The project handle. | /projects/{projectId}/… |
session(projectId, sessionId) |
The session handle. | /projects/{projectId}/sessions/{sessionId}/… |
connectors · connector(slug) |
Connector calls with a token that carries its project, such as a session token. Same methods as p.connectors (data plane) and p.connector(slug). |
/connectors/… |
github |
GitHub App installations and repository links, per account. | /projects/github/… |
gitBackend |
The instance git backend that holds Kortix-managed repositories. | /projects/git/backend |
billing |
Plan, credit balance, transactions, usage, subscription. | /billing/…, /usage/… |
notifications |
The caller’s inbox, preferences, Web Push and device tokens. | /notifications/… |
capture |
Kortix Capture: one account’s recorded screen, actions and audio, and the device sign-in approval. | /accounts/{accountId}/capture/…, /capture/… |
marketplace |
The public marketplace catalog and its sources. | /marketplace/… |
sandboxShares |
Public share links for one sandbox port. | /p/share |
connectStatus() |
Whether the managed OAuth connect provider is configured on this instance. | /connectors/connect-status |
mobileClientPolicy() |
{ minimum_version, store_url_ios, store_url_android }. null means the operator set no value. Needs no token. |
/clients/mobile |
validateToken() |
Checks a pasted API key. Never throws. | /accounts/me |
runtime() |
The runtime client of the process-wide active sandbox. Throws on a scoped client. Use s.messages() and the other session methods instead. |
sandbox |
kortix.projects
| Method | What it does |
|---|---|
list() · listForAccount(accountId) |
Projects you can open · projects in one account. |
get(projectId) · detail(projectId) |
Summary · full detail with config. |
provision(input) |
New project on a new Kortix-managed repository: { name, account_id?, seed_starter?, starter_template?: 'general-knowledge-worker' | 'minimal', marketplace_items?, source_item_id?, default_branch?, icon?, icon_glyph?, idempotency_key? }. |
create(input) · createRepo(input) |
From an existing repo_url · on a new GitHub repository. |
update(projectId, input) · archive(projectId) |
Change settings · archive. |
llmCatalog(projectId) · modelPicker(projectId) |
Full model catalog · the compact list for a model picker. |
modelAccess(projectId) · setModelAccess(projectId, { target: 'provider' | 'model', id, enabled }) |
Read · turn on or off one provider or model. Provider id kortix is the Kortix-managed models. Disabling the current default returns 409 cannot_disable_default. |
sandboxHealth(projectId) · sandboxTemplates(projectId) |
Sandbox build health · build templates. |
sessions(projectId) · createSession(projectId, input?) |
Same as p.sessions.list() · p.sessions.create(). |
Send idempotency_key (1 to 200 characters of A–Z a–z 0–9 . _ : -, one UUID per create) when a retry is possible. A repeat with the same key returns the first project (201, same project_id). A repeat while the first call still runs returns 409 provision_in_flight: retry with the same key. The key identifies the attempt, not the payload.
kortix.github and kortix.gitBackend
| Method | What it does |
|---|---|
github.getInstallation(accountId) · github.listInstallations(accountId) |
The account’s GitHub App installation · installations the user can reach. |
github.listLinkableInstallations(input) · github.linkInstallation(input) · github.saveInstallation(input) · github.deleteInstallation(accountId, installationId?) |
Installations the GitHub user may link · link · record · unlink one. |
github.listRepositories(accountId, installationId?) · github.listRepositoryBranches(…) |
Repositories the installation sees · their branches and default branch. |
github.linkRepository(input) |
Import a repository as a project. |
github.replaceProjectRepository(input) |
Point a project at another repository. Send expected_repo_url: a stale value returns 409. New sessions use the new repository. Running sessions keep their checkout. |
github.storeUserToken(input) |
Store the user’s GitHub authorization, needed to create a repository in a personal GitHub account. |
gitBackend.get() |
{ configured, kind: 'app' | 'pat' | null, owner }. Any signed-in user can read it. |
gitBackend.repositories({ search?, limit? }) |
Repositories under that owner. Self-host operators only; others get 403. |
Repositories, branches and change requests are explained in Git.
kortix.marketplace and kortix.sandboxShares
| Method | What it does |
|---|---|
marketplace.items(options?) · marketplace.item(id) · marketplace.itemFile(id, path) |
Browse the catalog · one item · one file of an item. |
marketplace.marketplaces() · marketplace.featured() |
All marketplaces · featured marketplaces. |
marketplace.sources.list() · .add(input) · .remove(id) |
Marketplace sources. |
sandboxShares.list(sandboxId) · .create({ sandboxId, port, ttl?, label? }) · .revoke(sandboxId, token) |
Public share links for one sandbox port. |
To install an item into a project, see Install from the marketplace.
Accounts, IAM, billing and notifications
kortix.auth
Methods: signUp, signInWithPassword, sendMagicLink, verifyOtp, signInWithProvider, exchangeCode, refresh, resetPassword, updatePassword, updateUserMetadata, signInWithSso, mfa.enroll, mfa.challenge, mfa.verify, mfa.unenroll, user, signOut.
session(options) returns a session store. It saves the tokens through a storage adapter you supply. It refreshes them 60 seconds before they expire. Pass its getToken to createKortix. See Headless sign-in.
kortix.accounts
| Method | What it does |
|---|---|
list() · get(accountId) · create({ name }) · updateName(accountId, name) · leave(accountId) |
Accounts you belong to, and their lifecycle. |
members(accountId) · invite(accountId, input) · updateMemberRole(accountId, userId, role) · removeMember(accountId, userId) |
Members and their account role. |
invites(accountId) · cancelInvite(accountId, inviteId) · resendInvite(accountId, inviteId) |
Pending invites. |
tokens.list(accountId?, { mine? }) · tokens.create({ accountId?, name, expiresAt?, projectId? }) · tokens.revoke(tokenId, accountId?) |
Personal access keys (kortix_pat_…). mine: true lists only yours. projectId binds the key to one project. |
connectedApps.list() · connectedApps.revoke(…) |
OAuth and MCP clients this person approved, across all accounts. Route /oauth/grants. |
secretResources.list · .create · .rotate · .retry · .remove · .grant · .revoke · .setAccess |
Account-level provider secrets and which projects may use them. |
branding.get(accountId) · .update(accountId, { app_name }) · .uploadAsset(accountId, kind, file) · .removeAsset(accountId, kind) · .reset(accountId) |
Organization branding. kind: logo, icon, favicon, or logo_dark, icon_dark, favicon_dark. |
audit.log(accountId, filters?) · audit.export(accountId, { format: 'csv' | 'jsonl', …filters }) |
The audit log, filtered by project, session, actor, source, outcome, request, resource, action or time · the same events as a file. |
audit.activity(accountId, { since?, until?, projectId?, actor?, client?, actorType?, credentialKind? }) |
Who was active and how often, per UTC day: per user, the active days, distinct active hours and events. client: 'browser' keeps only a person in a web page. |
audit.webhooks.list · .create · .update · .remove |
Signed webhooks that stream audit events to a SIEM. |
Account features are explained in Accounts & access, keys in API keys and tokens.
kortix.iam
Kortix has one grant record, the assignment: one principal (user, group, service_account, pending or project), one role, one scope (account or one project), an optional object (agent, skill, secret, app, trigger or connection) and an optional expiresAt.
| Method | What it does |
|---|---|
assignments.list(accountId, filter?) |
Live assignments. Filter by principalType with principalId, scopeType, scopeId, objectType, objectId, roleId, includeExpired. |
assignments.create(accountId, { principal, scope, object?, expiresAt?, roleId | roleKey }) · assignments.revoke(accountId, assignmentId) |
Grant · revoke. roleKey names a built-in role: owner, admin, member, manager, agent-user. |
permissions.list(accountId) · permissions.forRole(…) |
The permission catalog as data (action, scope_type, resource_type, delegable, description, area, level, implies) · one role’s permissions. |
roles.list · .create · .update · .setPermissions · .remove · .usage |
Custom roles. Without the account’s rbac entitlement the API returns 402 entitlement_required. |
groups.list · .get · .create · .update · .remove · groups.members.list · .add · .remove |
Groups and their members. |
agentIdentities(accountId) |
Agent principals, for binding a role to an agent. |
oauthClients.list · .get · .create · .update · .rotateSecret · .remove |
Sign in with Kortix clients. The secret is returned once, on create and on rotate. |
can(accountId, userId, { action, resourceType?, resourceId? }) · canBatch(accountId, userId, probes) |
Ask whether a principal may do one action · many, in one round trip, answers in input order. |
Gate a control in your UI with can or canBatch on the action the route checks, not on a role name. PROJECT_ACTIONS (root export) names every project action the API answers about, for example PROJECT_ACTIONS.PROJECT_GITOPS_MERGE is project.gitops.merge. The API answers an unknown action with 400. For triggers, probe TRIGGER_CONTROL_ACTIONS and pass the result to triggerControlsFrom(cans): it returns { canCreate, canFire, canUpdate, canDelete }, one flag per trigger route, each false until the probe allows it. Create, update and delete also need project.write, because their routes load the project for write first.
For change requests and the review inbox, probe REVIEW_CONTROL_ACTIONS. changeRequestControlsFrom(cans) returns { canOpen, canMerge, canClose }: open needs project.write + project.gitops.push, merge needs project.write + project.gitops.merge, close and reopen need project.write. reviewVerdictAllowed(item, verdict, cans) says whether to offer one verdict on one review item. Approving a change request merges it, so it follows canMerge. Every other verdict on a change request or a native item needs project.write + project.review.act. A connector call offers approve and reject to the viewer the API lets decide it: a project manager, or the person who launched the session when a person started it (not an API token or a service account). The API says so per call: can_resolve on each SessionAuditAction (getSessionAudit, and the session snapshot’s audit leg), and canResolve on a connector call’s review action (mapApiReviewItem). Show Approve and Deny only when it is not false. A viewer the API would refuse gets the review item with status waiting. resolvers (both shapes) says who decides instead: managers for a session started with an API token or a service account, else managers_and_launcher.
kortix.accountInvites
listMine() lists pending invites addressed to your email. describe(inviteId), accept(inviteId) and decline(inviteId) act on one invite. They need no account membership.
acceptAndLand(inviteId, invite?) accepts and returns { accepted, landing }. landing is where to open: { kind: 'project', projectId, accountId } for the first project the invite granted, or { kind: 'account', accountId } for a workspace invite. Pass the invite you already read (from listMine() or describe()): an accept that grants nothing, such as an admin invite, then lands in the first project the invite names. accountInviteLanding(accepted, invite?) is the same rule without the request, for an invite accepted earlier.
kortix.billing
| Method | What it does |
|---|---|
accountState(accountId?) · accountStateMinimal(accountId?) |
Plan, credit balance and limits · the short form. |
creditBreakdown(accountId?) · transactions(params?) · transactionsSummary(params?) |
Credit balance by bucket · transaction history · totals. |
usageHistory(params?) · usageRollup(params?) |
Usage over time · usage grouped by model, provider or day. |
sessionCosts.list({ accountId?, projectId?, limit?, offset? }) · sessionCosts.get(sessionId, options?) |
Usage records per session · one session’s model and compute ledger. |
tierConfigurations() |
The named amounts (Starter, Pro, Scale) and the minimum and maximum monthly amount. |
setPlan({ monthlyUsd, successUrl, cancelUrl, locale?, accountId? }) |
Subscribe to, or change, the credits plan. monthlyUsd is a whole dollar from 20 to 10,000; one credit is one cent. It resolves to { status: 'checkout_created', checkout_url } when the account has no paid subscription (redirect there), { status: 'updated' | 'unchanged' } when an existing subscription changed in place, or { status: 'payment_pending', client_secret } when a raise needs payment confirmation. A raise is charged now and grants the prorated credits at once; a cut applies at the next renewal. React: useSetBillingPlan() from @kortix/sdk/react. |
subscription.createPortalSession(returnUrl, accountId?) · .cancel(feedback?, accountId?) · .reactivate(accountId?) |
Manage the subscription. cancelScheduledChange and prorationPreview are retired and reject with ENDPOINT_RETIRED. |
credits.purchase(input) · credits.autoTopupSettings(accountId?) · credits.configureAutoTopup(input) |
Buy credits · read and set auto top-up. |
Before you render a purchase control, ask canOfferTopup(state) (root export) about the account state: true only when the plan may buy credits and the caller may change billing. canManageBilling(state) answers the second half alone. Both return false while state is undefined or null, so a member never sees a control the API answers with 403.
Plans and credits are explained in Plans & credits.
kortix.notifications
Every method needs a person’s credential. A service account gets 403.
| Method | What it does |
|---|---|
list({ limit?, before? }, { signal? }) |
Newest rows first: { notifications, unread_count, next_before }. limit is 1 to 50, default 20. Pass next_before as before for the next page. |
markRead({ ids }) · markRead({ all: true }) · markRead({ sessionId }) |
Mark 1 to 100 rows, all rows, or one session’s rows read. Returns { updated, unread_count }. |
preferences() · updatePreferences({ kinds }) |
Push and email per kind: { kinds, email_available }. A patch changes only the kinds it names. |
webPushPublicKey() · registerWebPushSubscription({ endpoint, keys }) · unregisterWebPushSubscription(endpoint) |
Web Push for this browser. Registration needs a browser sign-in: a personal access key gets 403. |
registerDeviceToken(input) · unregisterDeviceToken(token) · releaseDeviceToken(token, { backendUrl? }) |
A native app’s push token. releaseDeviceToken needs no credential: a signed-out app removes its token. |
Kinds (INBOX_NOTIFICATION_KINDS): turn_done, turn_error, question, permission, shared, automation_failed, automation_recovered. Defaults are in DEFAULT_NOTIFICATION_PREFERENCES. The inbox writes rows only for projects with the notification_center feature flag on. The flag is off by default. While it is off, s.watch() and s.setWatch() return 403 feature_disabled. The session handle adds watch(), setWatch(watching) and presence(…). See Notifications.
kortix.capture
Capture belongs to the account, not to a project. kortix.capture.account(accountId) binds one account. Every account call answers 403 capture_disabled while Capture is off for the account. Each member reads their own devices and timeline. Reading another member needs the capture.read_all permission and writes a capture.member_view audit event. Account owners and admins have it by default. See Capture.
| Method | What it does |
|---|---|
account(id).workspace.get() · .workspace.setEnabled(on) |
{ account_id, enabled, can_read_all, can_manage, updated_at } · turn Capture on or off (capture.manage). |
account(id).devices.list({ userId?, scope? }) · .devices.revoke(deviceId) · .devices.assetUrl(deviceId, name) |
Devices with live status (recording, paused, permission_missing, not_recording, or offline after 120 s without a status). scope: 'account' lists every member’s devices. · Revoke a device’s token. · A 5-minute signed URL of an action screenshot. |
account(id).policy.get() · .policy.set(policy) |
The account’s capture policy: layers, privacy, retention, recording, notice. A write needs capture.manage. |
account(id).timeline.get(query?) · .timeline.items(query?) · .timeline.days(query?) |
App and window runs for a UTC day or a window up to 31 days · frames, actions and audio in a window, 500 of each · the days with recorded items, newest first. |
account(id).search({ q, kinds?, app?, from?, to?, limit?, userId?, scope? }) |
Full-text search over on-screen text, actions and audio, newest first. |
account(id).frame(frameId, { userId? }) · .media(chunkId, { userId? }) |
One frame with its on-screen text and a 5-minute signed URL of its video · signed URLs of an item’s video and audio. |
deviceGrant(userCode) · approveDevice(userCode, accountId?) · denyDevice(userCode) |
The person’s half of the device sign-in. |
me.search(query) · me.timeline(query) · me.frame(frameId) |
Reads for the person the token acts for, in the token’s account. A credential that acts for no person gets 403 capture_no_human. |
React (@kortix/sdk/react): useCaptureWorkspace, useSetCaptureEnabled, useCaptureDevices (polls every 10 s), useRevokeCaptureDevice, useCapturePolicy, useSetCapturePolicy, useCaptureDays, useCaptureTimeline, useCaptureTimelineItems, useCaptureSearch, useCaptureChunkMedia, useCaptureDeviceGrant, useApproveCaptureDevice, useDenyCaptureDevice. Each account hook takes the account id first. A null id sends no request.
The project handle
kortix.project(projectId) returns p, with the project id bound to every method. Creating the handle sends no request.
Project
| Method | What it does |
|---|---|
get() · detail() · update(input) · archive() |
Read · full detail with config · change settings · archive. |
llmCatalog() · modelPicker() · modelAccess() · setModelAccess(change) · sandboxHealth() |
As on kortix.projects, project id bound. |
validateManifest(raw) |
Validate kortix.yaml text on the server, with the schema a merge uses. |
audit(options?) |
The project’s audit timeline. |
skills.create(input) |
Write a skill file to the default branch. |
attachments.upload(…) · .delete(…) · .createController(options?) |
Prompt attachments, and a controller that tracks uploads for a composer. |
onboardingComplete(…) |
Mark project onboarding done. |
connector(slug) · session(sessionId) |
One connector handle · the session handle. |
Sessions
| Method | What it does |
|---|---|
list(options?) |
One page of sessions as an array. |
listPage(options?) |
One page plus next_cursor. A cursor is valid only for the filters that issued it. |
create(input?) |
Create a session. The sandbox starts in the background. See Create a session. |
ensureWarm() |
Pre-create the session a present user is about to start. Ignore failures. |
| Option | Values |
|---|---|
scope |
visible (default) or project: the manager inventory with unavailable, warm and deleted sessions. |
limit · cursor |
1 to 200 rows · the next_cursor of the previous page. |
parent |
root (top-level sessions with child_count) or a session id (its children). |
startedBy |
me, others or automated. |
q |
Search over title, starter label, trigger slug and id prefix. Up to 200 characters. |
labels |
Sessions that carry every label. |
Each row carries parent_session_id and initiator: { type: 'member' | 'trigger' | 'channel' | 'api' | 'system', id, label }. sessionParentId(session) reads the parent.
Files, git and change requests
These methods read the project repository. To read and write files in a running session, use s.files.
| Method | What it does |
|---|---|
files.listDirectory({ ref?, path?, cursor?, limit? }) |
One folder level: { entries, truncated, next_cursor }. Use it to build a file tree. Without cursor or limit, one response carries up to 5,000 entries. With either, the response is a page of limit entries (default 500, at most 2,000). Pass next_cursor as cursor for the next page while it is a string: stop when it is null or absent (an API older than paging omits it). Every page reads the commit the first page read. |
files.list(options?) |
Every file under a path, recursively, cut at 1,000. A cut response carries X-Kortix-Truncated: 1. |
files.read(path, ref?) · files.history(path, { ref?, limit?, skip? }) |
A file at a ref · its commits. |
files.search(query, { content?, ref?, limit? }) |
Search file names, or contents with content: true. A content match carries line_number and line_text. |
files.archive(options?) · files.archiveRequest(options?) |
Download a tarball · the same as a request your host streams to disk. |
git.commits() · git.commit(sha) · git.commitDiff(sha) |
The log · one commit · its diff. |
git.branches() · git.versionDiff(…) |
Branches · the diff between two refs. |
git.inviteCollaborator(…) |
Invite a GitHub user to a Kortix-managed repository. |
gitToken() |
A fresh scoped push token for a Kortix-managed repository. A bring-your-own repository returns 409. |
changeRequests.list(status?, { limit?, cursor? }) · .get(crId) · .diff(crId) · .mergePreview(crId) |
Read change requests. list returns one page, newest number first: 50 by default, at most 500. Pass next_cursor back as cursor; it is null on the last page. |
changeRequests.open(input) · .update(crId, input) · .merge(crId, input?) · .close(crId, input?) · .reopen(crId, input?) |
Change request lifecycle. |
changeRequests.requestChanges(crId, input) |
Record feedback and, optionally, send it to the session that made the change. |
Read a large folder page by page:
let cursor: string | undefined;
do {
const page = await p.files.listDirectory({ path: 'src', limit: 500, cursor });
render(page.entries);
cursor = page.next_cursor ?? undefined; // null or absent ends the loop
} while (cursor);
Routes: /projects/{projectId}/files, /commits, /branches, /version-diff, /change-requests. See Git.
Review and approvals
| Method | What it does |
|---|---|
review.list(params?) · .get(id) · .submit(input) · .act(id, …) · .bulkAct(input) |
The Review page inbox: change requests, approvals and agent outputs. list returns one page ({ review_items, next_cursor }, 50 items by default, at most 200): Needs you, then Waiting, then Done. Filter with segment, kind and sessionId; pass next_cursor back as cursor. A walk is not a snapshot: an item decided between two pages can come back on a later page, so key the rows by review_item_id. get reads any item by id, including cr:<id> and a pending call:<id>. useReviewItemPages(projectId, query) (@kortix/sdk/react) is the same list as an infinite query; read its rows with reviewItemsOf(query.data). |
review.summary() |
{ needs_you_total, needs_you_by_session }: what waits on the caller, counted on the server. Read it for a badge or a per-session dot, never the list. useReviewSummary(projectId) polls it every 60 seconds. |
approvals.list(options?) · approvals.sessionsNeedingInput(options?) |
Connector calls that wait for a decision · sessions blocked on one or on an open agent question, with a count per session. useSessionsNeedingInput(projectId) (@kortix/sdk/react) reads the same list every 30 seconds, and again on each question event of an open session. |
approvals.resolve(executionId, 'approve' | 'deny', { note? }) |
Decide one call. note goes to the agent as an instruction. A decision covers that call only. |
Policies and approvals are explained in Policies and approvals.
Secrets, tokens and setup links
| Method | What it does |
|---|---|
secrets.list() · secrets.upsert(input) · secrets.remove(name) |
Secret metadata · create or update a write-only value with its delivery policy · delete. Each listed secret carries status for the shared value and mine.status for your own value: 'undecryptable' when the server can no longer decrypt it. upsert a shared value again; setPersonal or removePersonal your own. |
secrets.setStrategy(identifier, 'runtime' | 'egress' | 'broker' | 'denied', options?) |
Set the exposure: runtime is environment, egress is enforced, denied is none. broker keeps the value out of the session. |
secrets.broker(identifier, request) |
Send one policy-bound HTTPS request with the secret. Needs a session-scoped token. |
secrets.setPersonal(name, { value?, active? }) · secrets.removePersonal(name) |
Your own value for a secret, used in your sessions. active: false keeps your value but uses the shared one. removePersonal deletes your value. |
secrets.setGitCredential(input) |
The credential for a bring-your-own git repository. |
secrets.listProviderOAuth() · .startProviderOAuth(…) · .pollProviderOAuth(…) · .removeProviderOAuth(provider) |
Subscription logins for model providers, through a device-code flow. |
tokens.list() · tokens.create(input?) · tokens.revoke(tokenId) |
Project API keys. Kortix also mints one per session as KORTIX_TOKEN. |
setupLinks.requestSecret(input) · setupLinks.requestConnector(input) |
A link a person opens to enter a secret or connect an app, without full project access. A connector link works on composio and pipedream connectors, and on mcp connectors with one-click sign-in. |
Exposure policies and flags are explained in Secrets.
Connectors and policies
| Method | What it does |
|---|---|
connector(slug).run(action, args, options?) |
The action’s output. Throws ConnectorCallError, or ConnectorApprovalPendingError while a call waits for approval. |
connector(slug).call · .describe(action?) · .accounts() · .paginate(action, args, { next, maxPages? }) · .uploadAttachment(content, input) |
The raw result · schemas · callable accounts · a page iterator · stage a file. |
connectors.call(tool, args?, { account? }) · connectors.callAction(slug, action, args, options?) |
Call <connector>.<action> · the same with types from kortix connectors types. |
connectors.catalog(options?) · .tools() · .search(query) · .describe(tool) · .accounts(slug) · .uploadAttachment(…) |
Find and inspect callable tools and accounts. |
connectors.list() · .config(slug) · .create(input) · .remove(slug) · .sync() · .auth.discover(input) |
Configure connectors. auth.discover reads auth from an OpenAPI spec, a Postman collection or an endpoint. |
connectors.setName · .setSensitive · .setCredentialMode · .setCredential · .deleteCredential |
Connector settings and its stored credential. |
connectors.policies.get(slug) · connectors.policies.set(slug, policies) |
One connector’s tool policies. |
connectors.connections.list() · .listAll() · .reconcile(…) · .reconcileMember(…) · .updateCredential(…) · .revoke(…) · .activate(…) · .setDefault(…) · .rename(…) · .share(…) · .addComputer(…) |
Connected accounts: shared or private, defaults, labels, sharing and paired computers. |
connectors.pipedream.listApps · .listSections · .connect · .finalize |
Legacy Pipedream connect for OAuth apps. |
connectors.listMoves() · connectors.move(slug, { connectionId? }) |
Move a Pipedream or Composio connector to its Kortix MCP app. It keeps its slug. listMoves returns ConnectorMove[]. move returns ConnectorMoveResult: status: 'sign_in' with the connection_id to sign in on, or status: 'moved'. The root exports the same calls as listConnectorMoves(projectId) and moveConnector(projectId, slug, { connectionId? }). Needs the connectors_v2 feature flag. See Move a Pipedream or Composio connector to Kortix. |
connectors.discover.list · .sections · .detail |
The direct connector catalog. Needs the connectors_api_discover feature flag. |
policies.list() · policies.set(policies) |
Project policies. |
agentIdentities() · setAgentScope(agentName, scope) |
The project’s agents as principals · bind secrets and connectors to one agent. |
Accounts, the account a call uses, typed calls and errors are explained in Connectors and Call connectors from code.
Triggers and reminders
| Method | What it does |
|---|---|
triggers.list() |
Every trigger, with parse errors and triggers_paused. |
triggers.create(input) · triggers.update(slug, input) · triggers.remove(slug) |
Write a trigger. type is cron, webhook, monitor or event. name, type and prompt_template are required. |
triggers.fire(slug, { payload?, dryRun? }?) |
Run one trigger now. It works while triggers are paused. payload deep-merges over the manual payload at the root, for example { body: { … } }. dryRun: true writes nothing and returns ProjectTriggerDryRunResult: rendered_prompt, filter_matches, session_key and payload. A real fire returns status (fired, queued or deduped), session_id, command_id and run_id. |
triggers.runs(slug, { cursor?, limit? }?) · triggers.projectRuns({ status?, cursor?, limit? }?) |
One trigger’s runs · every run of the project, newest first, as { runs: ProjectTriggerRun[], next_cursor }. limit is 1–100, default 25. status is queued, running, fired, failed, skipped or rejected. A page can hold fewer than limit runs while next_cursor is set: follow next_cursor until it is null. |
triggers.replay(slug, runId, { confirmUnverified? }?) |
Fire the trigger again with the run’s stored payload and the current spec. Returns 409 run_in_progress while the run is queued or running, 409 payload_unverified for an unverified webhook payload unless confirmUnverified: true, and 422 payload_truncated for a payload over 64 KiB. |
triggers.setActivation(paused) |
Pause or resume every trigger of the project. |
triggers.eventApps() · triggers.eventTypes({ connector } | { app, source? }) |
Apps with events · the event types of one connector or app. |
reminders.list() · reminders.updateMany(…) · reminders.removeMany(…) |
Reminders on every session you can open: list, pause or resume, delete. One session’s reminders are s.reminders. |
Every field of create is in Triggers & reminders.
Apps
| Method | What it does |
|---|---|
list() · get(appId) · create(input) · update(appId, input) · remove(appId) |
App inventory and lifecycle. |
artifacts.register(input) · artifacts.uploadArchive(…) · artifacts.finalize(…) |
Upload a build. |
deployments.create(appId, …) · .list(appId) · .get(…) · .logs(…) · .remove(…) |
Releases and their logs. remove deletes one release that is not live. |
rollback(appId, deploymentId) · start(appId) · stop(appId) |
Move traffic to an earlier release · start · stop. |
machines.list(appId) · .start · .stop · .restart · .destroy(appId, machineId) |
Machines. Destroying one keeps its volume. |
scale(appId, { group: count }) |
Machines per process group, 0 to 20. |
exec(appId, input) |
Run one command in a machine. Needs project.app.admin. Kortix audits each call. |
volumes.list(appId) · .create · .update · .destroy(appId, volumeId, confirmName) · .snapshots · .snapshot · .restore(appId, volumeId, snapshotId) · .fork |
Persistent volumes and their snapshots. |
access.get(appId) · access.update(appId, …) · access.session(appId) · access.agents(appId) |
Who can open the App · an access session · agents granted the App in kortix.yaml. |
token(appId) |
A 15-minute sign-in token for the App that names the caller. Needs the member_tokens capability. |
authEnv(appId) |
KORTIX_AUTH_ISSUER, KORTIX_AUTH_AUDIENCE and KORTIX_AUTH_JWKS, to verify App tokens without a network call. |
Apps are generally available in every project. Apps routes still require project membership and the appropriate Apps permission. Routes: /projects/{projectId}/apps/…. See Build on Apps from TypeScript.
Gateway and models
| Method | What it does |
|---|---|
gateway.logs(options?) · gateway.log(logId) |
Model request logs. |
gateway.overview(days?) · .series(days?) · .breakdown(days?) · .sessions(days?) · .errors(days?) |
Rollups over a window. |
gateway.budgets() · .setBudget(input) · .deleteBudget(budgetId) |
Budgets. |
gateway.keys() · .createKey(name) · .revokeKey(keyId) |
Gateway API keys for your own tools. |
gateway.routing.get() · .set(policy) · .reset() · .preview(input) |
The routing policy: default model, vision model and ordered fallbacks · resolve a route without a model call. |
gateway.playground(prompt, models, system?) |
Run one prompt against 1 to 6 models. |
modelDefaults.get() · .set(input) · .clear(params) |
Default models per account, agent and project. |
setDefaultAgent(agentName) |
Set default_agent in kortix.yaml. The agent must be declared and enabled. |
Routing, fallback and budgets are explained in Models.
Channels
| Method | What it does |
|---|---|
channels.slack.installation() · .mode() · .manifest() · .connect(input) · .completeInstall(input) · .disconnect() |
The Slack app of the project. |
channels.slack.getFile(url) · channels.slack.uploadFile(input) |
Download or upload a Slack file through the server. The bot token stays on the server. |
channels.teams.completeInstall(input) |
Finish a Microsoft Teams install as the signed-in user. |
channels.email.installation(connectorSlug?) · .mode() · .connect(input) · .disconnect(connectorSlug?) · .updatePolicy(…) |
The email channel and its send and reply policy. |
See Slack, Teams & email.
Access
| Method | What it does |
|---|---|
access.list() · access.invite(email, role, expiresAt?) · access.update(userId, role) · access.revoke(userId) |
Members of the project. Roles: manager, member. |
access.pendingInvites() · .resendInvite(inviteId) · .revokeInvite(inviteId) |
Pending invites. |
access.requests() · .approveRequest(id) · .rejectRequest(id) |
Access requests. |
access.groupGrants() · .attachGroupGrant(…) · .updateGroupGrant(…) · .detachGroupGrant(groupId) |
Groups with project access. |
access.resourceGrants.list() · .create(input) · .remove(grantId) |
Access to one object, such as one agent or skill, for a member or a group. |
See Project access and Grant access to one object.
Sandbox
| Method | What it does |
|---|---|
sandbox.list() · sandbox.snapshots() |
The project’s sandboxes · built snapshots. |
sandbox.rebuildSnapshot(slug?) · sandbox.fixWithAgent() |
Rebuild a snapshot · ask an agent to fix a failed build. |
sandbox.createTemplate(input) · .updateTemplate(…) · .removeTemplate(templateId) · .buildTemplate(templateId) |
Sandbox templates. |
sandbox.setProvider(provider | null) |
Pin the sandbox provider, or null for the platform default. Returns kind: 'project' when the change applies at once, or kind: 'preparation' while the new provider’s image builds. The current provider serves until activation. |
See Sandboxes & previews.
Settings and feature flags
p.updateFeatureFlag(key, enabled) sets one flag for the project with PATCH /projects/{projectId}/features. key is one of FEATURE_FLAG_KEYS (type FeatureFlagKey). enabled: null clears the override. The caller needs project.settings.write. A route behind a flag that is off returns 403 { error, code: 'feature_disabled', feature }: test it with isFeatureDisabledError(error) and read the key with featureDisabledKey(error). See Feature flags.
The session handle
kortix.session(projectId, sessionId) (or p.session(sessionId)) returns s. Creating the handle sends no request. Runtime methods call ensureReady() first. Each one is explained in Sessions from code.
| Area | Methods |
|---|---|
| Lifecycle | get(), update(input), delete(), restore(), start(), startOrThrow(), restart(), stop() |
| Readiness | ensureReady({ readyTimeoutMs?, session? }), health() (never throws on a non-2xx status) |
| Prompts | send(text, { model?, agent? }), abort(), prompts.create · .list · .remove · .retry · .edit · .interrupt · .hold |
| Live events | stream({ onEvent, onGapRehydrate?, signal? }) returns a handle with close() |
| Conversation | messages({ conversationId?, limit?, before? }), pending(), answerPermission(requestId, 'once' | 'always' | 'reject', message?), answerQuestion(requestId, answers | null), compact(model?), rewind(messageId), restoreRewind() |
| Server-side reads | turn(), log(query?) (one page of the stored session log, null when the session has none; answers while the sandbox is stopped), transcript(options?) and transcriptSync(options?) (deprecated: the v1 transcript; read log), messageAuthors(), modelUsage(), cost(), audit(limit?) |
| Files and previews | files.list · .read · .readBlob · .status · .findFiles · .findText · .upload · .write · .create · .copy · .remove · .mkdir · .rename, previews(), previewUrl(port, path?), proxyUrl(url), sandboxPortUrl(port), attachments.upload · .read |
| Model, agent and scope | setModel(model), setAgent(agent) (every later send on this handle uses it, until you change it; send(text, { model, agent }) overrides it for one prompt), changeModel('provider/model') (persisted, restarts the runtime), scope(), rescope(scope), providerSecretPool.list · .get · .set |
| Config | configState(), reloadConfig(input?), reloadConfigStream(…) |
| Sharing and people | setSharing(intent), publicShares.list · .create · .revoke, participants(), watch(), setWatch(watching), presence({ tab_id, active, alerts? }, { keepalive? }) (answers awake_until and awake_cap_at) |
| Git and reminders | commit(input?), reminders.list · .create · .update · .remove |
Framework-free modules
The root @kortix/sdk exports these modules next to the client. Use them for one operation without the facade, or for pure helpers.
| Module | Names | What it does |
|---|---|---|
| Files | files, FileExistsError, uploadNativeFile |
File operations on the process-wide active sandbox, with the same methods as s.files. A host with several sessions uses s.files. |
| Session health | getSessionHealth(runtimeUrl?, init?), isRuntimeReady(health), detectLocalhostUrls, rewriteLocalhostUrl, proxyLocalhostUrl, parseLocalhostUrl, buildWebProxyUrl |
Probe a runtime and rewrite an agent’s localhost URLs into reachable proxy URLs. |
| Event stream | openEventStream, narrowChatEvent |
The live stream behind s.stream, and a narrower for chat events. |
| Auth | authenticatedFetch, getAuthToken |
A fetch that adds the token from your getToken. |
| API client | backendApi |
The typed HTTP client under every method. Use it only for an endpoint with no method. |
| Errors | ApiError, isFeatureDisabledError, featureDisabledKey |
Error types and checks. |
| Composer lists | projectConfigAgentsToRuntimeAgents, composerSelectableAgents, resolveComposerAgent, pickerProviderList, flattenModels, createModelVisibility, modelInDefaultView, resolveComposerModel, resolveModelDefault, sessionModelCandidates, latestPromptModel, modelRefToKey |
Build an agent picker and a model picker with the rules of the Kortix web app. Every function is pure. |
| Transcript export | formatTranscript(session, messages, options?) |
One session as a Markdown string. |
| Sandboxes by id | restartSandbox(id), stopSandbox(id), getSandboxById(id) |
Act on a session’s sandbox with only its session_id. One GET /v1/sessions/:id finds the project, however old the session is. A provider external id still works, but only for a session on its project’s first sessions page. With the project id, use s.restart(), s.stop() and s.get(). |
| Sandboxes, every project | getSandbox(), ensureSandbox(), listSandboxes(id?) |
Read each project’s first sessions page only (default 50 rows). They need a project to find older sessions: use p.sessions.listPage(). |
Files
import { FileExistsError, files } from '@kortix/sdk';
const { content } = await files.read('/workspace/README.md');
await files.upload(file, '/workspace/uploads', file.name, {
onProgress: ({ loadedBytes, totalBytes }) => console.log(loadedBytes, '/', totalBytes),
});
try {
await files.create('/workspace/notes.md');
} catch (error) {
if (error instanceof FileExistsError) console.log('exists:', error.path);
else throw error;
}
uploadsends a file of 8 MiB or less in one request. A larger file goes in 8 MiB chunks, and each chunk is checked on arrival.onProgressruns after each chunk.write(path, blob)replaces the file atpath. It fails whenpathis a folder.create(path)makes an empty file and never overwrites. When anything exists atpathit throwsFileExistsError(status409,codeFILE_EXISTS).uploadNativeFile({ uri, name, type? }, targetPath?, baseUrl?)uploads a React Native device file in one request. The bytes stream fromuriand never enter the JS heap.
Session health
getSessionHealth() never throws on a non-2xx status. It returns { status, ok, health, body, hop, upstreamStatus }. isRuntimeReady(health) is true when the runtime accepts work. s.health() is the same probe for one session.
Custom transcript rendering
Pure functions that turn session messages into renderable parts. Use them to build your own chat view. They have no React and no DOM.
Classify parts and turns
classifyPart(part) turns one part of the Kortix transcript format (kortix.transcript.v1, KORTIX_TRANSCRIPT_SCHEMA) into a ClassifiedPart, keyed by kind:
kind |
Fields |
|---|---|
text |
id, text, synthetic. Do not render a synthetic part as a chat bubble. |
reasoning |
id, text |
tool |
id, tool: ToolView |
file |
id, filename?, mime, url, isImage, isPdf |
subtask |
id, description, agent, prompt, model? |
patch |
id, hash, files, fileCount |
snapshot |
id, snapshot |
agent |
id, name |
retry |
id, attempt, message, createdAt |
compaction |
id, auto, overflow, tailStartId? |
step |
id, phase: 'start' | 'finish', snapshot?, reason?, cost?, tokens? |
unknown |
raw: a part type this SDK version does not know. It never throws. |
ToolView fields: name, kind (from toolKind), title, status (pending, running, done, error), input, output, error, outputParsed (the parsed JSON output, for output up to 256 KiB), outputText, files, diff, diagnostics, answers, childSessionId. A tool that reports success: false inside a completed state has status: 'error'. toToolView(part) builds a ToolView from one tool part or one session-log ToolCallBlock. Read these fields, not a harness’s own metadata: the same card then renders every harness.
classifyTurn(message) returns { parts, error?, isEmpty } for one assistant message. isEmpty is true when the turn has no error and no visible part.
import { classifyTurn, toolViewModel } from '@kortix/sdk';
for (const part of classifyTurn(message).parts) {
if (part.kind === 'text' && !part.synthetic) render(part.text);
if (part.kind === 'tool') {
const view = toolViewModel(part.tool);
if (view.kind === 'shell') render(view.command, view.stdout, view.exitCode);
}
}
Tools
toolKind(name)maps any harness’s tool name to oneToolKind:read,list,glob,grep,write,edit,apply_patch,bash,pty,web_search,webfetch,media,show,task,delegate,sessions,todowrite,question,memory,connectors,automations,projects,skill,context,retiredorother.toolInfo(name)returns{ label, category }.getToolInfo(name, input)returns{ icon, title, subtitle }for a tool card. An unknown name falls back tohumanizeToolName(name).inputPath(input)reads a file tool’s path fromfilePath,file_pathorpath.toolViewModel(tool)returns one shape per tool family:
kind |
Fields |
|---|---|
web-search |
query, results?, answer?, error? |
shell |
command, stdout?, exitCode? |
file-read · file-write |
path, preview? |
file-edit |
path, diff? (lines of { type: 'added' | 'removed' | 'unchanged', text }) |
search |
pattern, matches? |
task |
description, agent? |
todo |
items |
question |
questions, answers? |
generic |
label, inputPretty?, outputPretty?. Every other tool. Always safe to render. |
Turns, status and helpers
| Names | What they do |
|---|---|
groupMessagesIntoTurns(messages) · collectTurnParts(turn) |
Pair each user message with the assistant messages that answered it · all parts of one turn. |
findLastTextPart(parts) · turnHasSteps(parts) |
The final answer text · whether the turn has tool, compaction, snapshot or patch parts. |
getTurnStatus(parts, childMessages?) · getWorkingState(…) · formatDuration(ms) |
A status line such as Reading files.... Pass childMessages to show a sub-agent’s activity. |
getTurnError(turn) · getChildSessionError(messages) · unwrapError(error) |
Turn and sub-agent errors, normalized. |
getRetryInfo(status) · getRetryMessage(status) |
Retry state { attempt, message, next, details? } · the full retry message. |
getChildSessionId(part) · getChildSessionToolParts(messages) · rootRuntimeSession · directSubsessions · runtimeSessionsOf · projectSessionForRuntimeId |
Connect a delegating tool call to its child conversation. |
getPermissionForTool(permissions, callID) · getHiddenToolParts(…) · isToolPartHidden(…) |
Match a pending permission or question to its tool call. |
isTextPart, isToolPart, isReasoningPart, isFilePart, isAgentPart, isCompactionPart, isSnapshotPart, isPatchPart, isStepPart, getPartText |
Type guards. |
sortSessions(now) · childMapByParent(sessions) · allDescendantIds(map, id) |
Session list order and hierarchy. |
getFilename, getDirectory, relativizePath, stripAnsi, formatTokens |
Formatting. |
The grouping and status functions accept the structural types PartLike, MessageInfoLike, TurnLike, ToolStateLike and SessionStatusLike, so your own message shapes work. Never mint a message id by hand: use mintWireMessageId().
Session log types
The root exports the types of the Kortix session log, the harness-neutral record of a session, schema kortix.session/2. Kortix stores every session as a session log. The SDK exports the types and three constants, with no validators. In React, useSessionLog and useSessionLogThread return a session’s log once the session reads it: see Read the session log.
| Name | What it is |
|---|---|
SESSION_LOG_SCHEMA · SESSION_LOG_MINOR |
'kortix.session/2' · 1 |
SessionLog |
The record: threads, todos, pending questions and permissions, harness history, restore_grade. |
SessionLogThread · SessionLogMessage |
One thread (the main thread or a sub-agent) · one message with seq, role, kind, status, model, usage, blocks. |
SessionLogBlock |
The union of TextBlock, ReasoningBlock, AttachmentBlock, ToolCallBlock, CompactionBlock, SubtaskBlock, StepBlock, HarnessBlock, keyed by type. |
ToolResultContent, CompactionLayoutEntry, ThreadInteraction, ModelRef |
Parts of the records above. |
KNOWN_TOOL_KINDS · KnownToolKind · SessionLogToolKind |
The tool kinds every reader renders: shell, read, write, edit, patch, list, glob, grep, web_fetch, web_search, todo, task, question, plan, mcp, other · the tool kind of a block, any string. Render an unknown kind as other. |
SessionLogTodo |
One todo item. |
RestoreGrade · GradeCounts |
How faithfully the record restores (native, converted or partial) · what is missing. |
A text over 256 kB is stored by reference. This applies to a TextBlock and to a text item of a tool result (ToolResultContent). Its text holds the first 16 kB, bytes holds the UTF-8 length of the full text, and ref is kortix-attachment://<project_id>/<session_id>/<attachment_id>. kortix.session(projectId, sessionId).attachments.read(attachmentId) returns the full text as a Blob (text/plain; charset=utf-8), also while the sandbox is stopped. The v1 transcript reads return the full text.
import type { SessionLog, SessionLogBlock } from '@kortix/sdk';
function toolCalls(log: SessionLog): SessionLogBlock[] {
return log.threads.flatMap((t) => t.messages.flatMap((m) => m.blocks)).filter((b) => b.type === 'tool_call');
}
Tool calls from v1 parts (transitional)
Three functions bridge a host that renders blocks while its rows are still v1 (useSessionMessages). All three are transitional.
| Name | What it does | Removed |
|---|---|---|
toolCallBlock(part) |
The ToolCallBlock of a v1 tool part: the block fromV1 builds for it. An open call stays open (pending or running, no result). A call that streams its input has the text so far as its input. The same part object returns the same block. |
In the SDK major that removes the v1 rows. |
toolCallMetadata(call) |
The harness’s display metadata of a tool call: the whole state.metadata of the v1 part it came from, such as bash command, read loaded, task sessionId or edit filediff. A record a harness wrote natively carries its details (result.details, else the adapter’s own ext: pi keeps them in ext.pi.data.details) and exit (from result.exit_code). One shared empty object when there is none. |
When the session-log schema carries display metadata. |
sessionLogOfV1Rows(sessionId, runtimeSessionId, rows) |
The fromV1 live view of one runtime session’s v1 rows: the SessionLog useSessionLogThread returns on v1 (one root thread, the runtime session id as its id, the open turn kept open). For rows a host reads without a useSession, such as a sub-agent’s chat. A message keeps its identity while its row object is unchanged. |
In the SDK major that removes the v1 rows. |
toToolView, getChildSessionId, partOutcome, contextToolTrigger and shouldShowToolPart accept a ToolCallBlock as well as a v1 tool part, and return the same answer for both.
import { getChildSessionId, toolCallBlock, toolCallMetadata } from '@kortix/sdk';
const call = toolCallBlock(part); // part: a v1 tool part from useSessionMessages
const child = getChildSessionId(call); // a running task's child session
const command = toolCallMetadata(call).command; // bash
Reading a tool call
What a host draws a ToolCallBlock with. Block-native: they stay.
| Name | What it does |
|---|---|
toolCallStatus(call) |
The status in the words a renderer compares: pending, running, completed or error. |
toolCallRawOutput(call) |
A completed call’s output text as the tool returned it (its result’s text items, joined); '' for any other call. One string per call object. |
toolCallError(call) |
A failed call’s error text: its semantic error (result.error.message), else its result text, as toV1 gives a v1 part’s state.error. A harness that closes a failed call with is_error and the tool’s text only still shows that text. undefined for a call that did not fail. |
subAgentCalls(thread) |
The tool calls a sub-agent row lists: every shown call (shouldShowToolPart) of the thread’s replies, in order. One array per thread object; the same empty array for null. |
Block turns
The turn model over session-log blocks: what a transcript renders a thread from. Each name replaces a v1 helper over MessageWithParts (see Turns, status and helpers); the v1 helpers stay until the next major.
| Name | What it does | Replaces |
|---|---|---|
sessionLogTurns(thread, options?) |
One SessionLogTurn per user message: id, user, assistant (the replies, by reply_to then order), partial (the prompt is not loaded), blocks (every reply block), and the derived fields status (pending, else the last reply’s status), error (code, message, details, raw), cost (the replies’ usage priced with the platform markup; a compaction’s summary usage is a reply’s; a message converted from v1 is priced over its v1 parts, exactly as getTurnCost prices them), compaction (block, hasContent, inFlight), threads (the sub-threads it spawned), plan (owns the plan card), shell (shell mode’s bash call), response (the last text block), attachments, live, queued, retrying. Options: busy, liveMessageId, held (the prompts the inbox holds, in queue order), threads, pricing. A turn keeps its identity while its messages and derived fields do. A message out of context keeps its turn; hideOutOfContext: true leaves out every message with in_context: false that is not streaming (a failed attempt, an aborted empty reply, an old root’s history). |
groupMessagesIntoTurns, collectTurnParts, getTurnError, getTurnErrorDetails, getTurnErrorRawText, getTurnCost, compactionTurnInfo, getShellModePart, findLastTextPart, isAttachment, getWorkingState, resolveWorkingTurn, hasRetryingAssistantTurn, getChildSessionId |
sessionLogTurnSegments(blocks, options?) |
The segments of a turn: burst (a run of non-text blocks), text, standalone (a show or sub-agent call, or a call named in standaloneCallIds), show-group (adjacent show calls). |
segmentTurn, groupShowSegments |
splitSessionLogTurnWork(segments, { working }) |
The settled-turn fold: work, hoisted (the show deliverables), final (the last text). null while the turn works or has no work. |
splitTurnWork |
sessionLogBurstSteps(blocks) |
The rows of a burst: thought (a run of reasoning; running only while its message streams), block, group (same-family calls, with family, label, status). An answered question is its own row. |
mergeBurstSteps, stepLabel |
sessionLogCallStatus(call, live) |
The status a call renders with: pending, running, complete, error, or interrupted. An open call, or one the closure rule closed (result.synthetic, or result.error.code 'interrupted'), is running (started) or pending on a live turn and interrupted on any other. |
new |
sessionLogOpenCall(call) |
The call in the open shape sessionLogCallStatus(call, true) names: a call the closure rule closed reads running (started) or pending, without its result; every other call is itself. One object per call. Render a thread’s calls through it so the v1 live view and the v2 page draw one call the same; sessionLogCallStatus(call, turn.live) says whether it is interrupted. |
the host’s open-call shim |
sessionLogBurstSummary(blocks) |
A burst’s step counts: total (a run of reasoning is one), failed, completed, hasPlumbing. |
burstSummary |
sessionLogBurstRunning(blocks, { working, trailing? }) |
The burst is in progress: never once the turn stops working; the trailing burst for the whole working turn; another one while a call is open or a thought is still written. | the host’s burstIsRunning |
sessionLogShowGroupCall(calls) |
A show-group segment as one show call: the first call’s ids, input.items the carousel (a failed call keeps its slot as an error item), running while a call is open, else complete. |
the host’s mergeShowParts |
sessionLogAnsweredQuestionCall(call, answers, input?) |
A question call as answered with answers the harness has not confirmed yet: complete, the answers in its metadata (toToolView(call).answers), input replaced when given. |
the host’s synthetic answered part |
sessionLogRetrying(thread) |
The thread’s last reply is open with a retryable provider error: its turn’s retrying, read without grouping turns, so the busy state a host passes as busy can include it. False on a native record (no retry flag). |
hasRetryingAssistantTurn |
sessionLogActivityText(turn, childMessages?) |
The activity line of a live turn (“Reading files…”); a running task reads its sub-thread’s messages. | getTurnStatus |
sessionLogThreadMetrics(thread, options?) |
One thread’s cost (every reply’s usage priced: its reported cost, else the lookup, with the platform markup), messages (all, user, assistant; a compaction counts as its user marker and, once it is no longer streaming, its summary reply) tokens (input, output, reasoning, cacheRead, cacheWrite: every priced reply’s usage summed) and context (the last reply, an assistant message or a compaction, with tokens: its usage is the context size). Options: pricing. What a context modal’s overview and its sub-agent rows show. |
getSessionCost over v1 rows, a host’s own token and message counts and last-reply lookup |
classifySessionLogMessage(message) |
classifyTurn of one session-log message: its blocks as ClassifiedParts and its error as TurnError (the gateway’s own sentence, which message.error.message does not carry for a converted record). A compaction is one message: the marker’s parts, then the summary’s. For a host whose renderers take ClassifiedParts (the TUI, the white-label demo). |
classifyTurn over v1 rows |
sessionLogPromptMessage(prompt, threadId) |
The user message of a queued inbox prompt (SessionPrompt) the thread has not placed: id message_id, else queued-<prompt_id>. Append it after the thread’s messages and name it in held. |
the host’s synthetic row |
isSessionLogRestoreNote(message) |
The message is the note a partial restore leaves for the agent: a user message whose text block id starts with prt_kortix_restore_g (on v2 it also carries origin: 'notification'; the v1 transcript has no origin). Render it as a notice, not as a person’s prompt. |
new |
sessionLogRestoreNotice(session) |
What a partial restore could not bring back, for the person: { generation, missing }, missing a list of { kind, count } (attachment, text, tool_input, tool_result, cut_point, other) with counts above zero. Reads restore_grade, grade_counts and ext.kortix.data.restore of the session record (useSessionLog). Null unless the grade is partial and a restore wrote the record. |
new |
sessionLogHarnessSwitches(history, turns) |
The switches harness.history records, by the id of the turn each lands on (the first turn whose prompt seq is at or above from_seq): { harness, previous, version, at }. A new version of the same harness is no switch. A switch inside a page not loaded yet is left out. |
new |
sessionLogReferencedTexts(messages) |
The texts the API stored by reference (a text over 256 kB keeps a 16 kB preview, plus ref and bytes) that a transcript renders, text blocks and tool results’ content items (not model_content), once per ref: { ref, bytes }. Read one with fetchSessionAttachment(ref). |
new |
sessionLogWithFullTexts(thread, texts) |
thread with each text whose ref is a key of texts replaced by the whole text, without ref and bytes. Untouched messages, and the thread when nothing changes, keep their identity. |
new |
sessionLogFullTextAction(refs) |
How a host offers the whole of refs: 'inline' up to SESSION_LOG_FULL_TEXT_INLINE_MAX_BYTES (1 MiB) in all, else 'download' (a multi-MB text rendered as markdown stalls the page). |
new |
sessionLogFullTextPlan(refs) |
Which of refs show inline and which download: the smallest texts fill the 1 MiB inline budget first, only the rest download ({ inline, download }, both in record order). Nothing downloads exactly when sessionLogFullTextAction says 'inline'. |
new |
A pending permission or question matches a call by call.call_id: getPermissionForTool(permissions, call.call_id), getQuestionForTool(questions, call.call_id). freshSendHint and fallbackBusyRowAfterTurnId accept a SessionLogTurn[] as well as v1 turns, and stepLabel any session-log block (a non-call block reads by its type).
import { sessionLogTurns, sessionLogTurnSegments, sessionLogCallStatus } from '@kortix/sdk';
const turns = sessionLogTurns(thread, { busy, liveMessageId: working.turnId, held: heldIds, threads: log.threads });
for (const turn of turns) {
for (const segment of sessionLogTurnSegments(turn.blocks)) {
if (segment.kind === 'standalone') render(segment.block, sessionLogCallStatus(segment.block, turn.live));
}
}
Deprecated names
Each name below is marked @deprecated and stays until the next major version. Your editor flags each call.
| Deprecated | Use instead |
|---|---|
p.updateExperimentalFeature(key, enabled) · updateExperimentalFeature · ExperimentalFeatureKey · ExperimentalFeatureView |
p.updateFeatureFlag · updateFeatureFlag · FeatureFlagKey · FeatureFlagView |
s.runtime |
The session methods: messages, pending, answerPermission, answerQuestion, compact, send, abort, rewind |
OpencodeClient · OpencodeClientConfig · createOpencodeClient · OpenCodeEvent · OpencodeAgentConfig · ProjectOpenCodeSession · OpenCodeProjectInfo |
RuntimeClient · RuntimeClientConfig · createRuntimeClient · RuntimeEvent · RuntimeAgentConfig · ProjectRuntimeSession · RuntimeProjectInfo |
projectConfigAgentsToOpenCodeAgents · parseOpenCodeErrorPayload · formatOpenCodeRuntimeError |
projectConfigAgentsToRuntimeAgents · parseRuntimeErrorPayload · formatRuntimeError |
OpenCodeConfigIssue · OpenCodeConfigInvalidError · getOpenCodeConfigInvalidError · isOpenCodeConfigInvalidError |
RuntimeConfigIssue · RuntimeConfigInvalidError · getRuntimeConfigInvalidError · isRuntimeConfigInvalidError |
p.sessions.claimWarm(input) · claimWarmProjectSession |
Open the session from p.sessions.ensureWarm() and prompt it |
p.connectors.setAuthorizationStrategy · setConnectorAuthorizationStrategy |
The call changes nothing. To create an account under one owner, pass owner to connectorConnect. |
getConnectionPolicies · setConnectionPolicies |
p.connectors.policies.get · p.connectors.policies.set |
pipedreamConnect · pipedreamFinalize |
connectorConnect · connectorFinalize |
listDiscoverIntegrations · getDiscoverIntegration · DiscoverIntegration* types |
listDiscoverConnectors · getDiscoverConnector · DiscoverConnector* types |
SessionScopeResult · toWorkspaceRelative |
SessionScope · toDaemonPath |
setCachedAuthToken · setBootstrapAuthToken |
Return the token from your own getToken |
claimNext, enqueue, createSessionQueue and the other @kortix/sdk/message-queue names |
s.send, or useSession().send in React |
wireIdClock, wireIdClockDelta, maxWireIdClock, newestWireIdClock, mintWireMessageIdAbove and the WIRE_ID_* constants |
mintWireMessageId. Message ids are opaque. |
billing.checkout.createSession · billing.checkout.confirmSession · createPerSeatCheckout · claimPerSeatBilling |
billing.setPlan(input). All reject with ENDPOINT_RETIRED. |
billing.subscription.scheduleDowngrade · billing.subscription.cancelScheduledChange · billing.subscription.prorationPreview |
billing.setPlan(input), or billing.subscription.createPortalSession. They reject with ENDPOINT_RETIRED. |
Other deprecated root names wrap routes that the API does not serve. They reject with ENDPOINT_RETIRED, resolve null, or return 404, and they have no replacement.