SDK reference
The @gitterm/sdk TypeScript client for workspaces, agent runs, connections, sandboxes, and errors, with real method names and options.
@gitterm/sdk is the TypeScript client for the Gitterm API. Use it to create cloud workspaces, run an agent in them, pass the agent's questions to a person, and run commands in sandboxes. The CLI, the OpenCode plugins and the chat bots are built on it. Use this page to look up a method or option.
Install
npm install @gitterm/sdk
# or
bun add @gitterm/sdkRequires Node 22.12 or later, or Bun.
Client setup
import { createGittermClient } from "@gitterm/sdk";
const client = createGittermClient({ token: process.env.GITTERM_API_TOKEN });With no token from any source, calls fail with NOT_LOGGED_IN. For a self-hosted deployment behind the bundled path-routing proxy, use the /api path, for example https://gitterm.example.com/api. client.serverUrl and client.auth.status() show which server and account you are using.
Tokens and scopes
Create tokens under API tokens in the dashboard, or sign in with gitterm login. A dashboard token can be limited to these scopes:
To get a token from your own app, use the device-code flow:
import { DEFAULT_GITTERM_SERVER_URL, loginWithDeviceCode, saveConfig } from "@gitterm/sdk";
const serverUrl = process.env.GITTERM_SERVER_URL ?? DEFAULT_GITTERM_SERVER_URL;
const { token } = await loginWithDeviceCode(serverUrl, {
onCode: ({ verificationUri, userCode }) =>
console.log(`Visit ${verificationUri}, enter ${userCode}`),
});
await saveConfig({ serverUrl, token, createdAt: Date.now() });Workspaces
A workspace argument is a workspace id, or any object with an id.
Requests through a workspace's URL count as activity and hold off idle pausing. If your own code only checks on a workspace, add the header PASSIVE_REQUEST_HEADER (exported by the SDK, value "1") to those requests: they are routed as usual but not counted. runs.get and the other run reads already do this.
Waits accept { timeoutMs, pollIntervalMs, signal }. On the server, ensureRunning waits at most 240000 ms.
For statuses, idle pausing and what survives a pause, see Lifecycle.
Create options
Setup commands can use $WORKSPACE_REPO_DIR for the checkout path on any provider. To change secrets or variables later, create a new workspace.
Runs
A run sends a prompt to an OpenCode workspace and tracks it to the end. T3 Code workspaces don't support runs. A run argument is an AgentRun, or any { workspaceId, id }.
const run = await client.runs.create({
workspace,
idempotencyKey: "review-pr-42",
prompt: "Review PR #42 and fix the failing tests.",
});
const result = await client.runs.result(run);
console.log(result.finalText);Create input
runs.create() does not resume a paused workspace. Call workspaces.ensureRunning() first.
Statuses and events
Run statuses: pending, running, retrying, awaiting_input, completed, failed, cancelled.
Event types from runs.events(): run.status, input.required, input.resolved, run.completed, run.failed, run.cancelled.
for await (const event of client.runs.events(run)) {
if (event.type === "input.required") {
await client.runs.respond(run, {
requestId: event.request.id,
reply: await askHuman(event.request),
});
}
}askHuman is your own function: show the request to a person and return their reply. Replies look like:
{ type: "permission", response: "once" } // or "always" or "reject"
{ type: "question", answers: { [question.key]: ["Selected label"] } }
{ type: "question", reject: true }A run waiting for input keeps the workspace awake until you answer, cancel the run, or its deadlineAt passes. To avoid prompts in unattended runs, allow tools in opencode.config.permission when you create the workspace.
Integrations
How each integration works: Connections.
Catalog and credentials
Workspace client
Inside a workspace, createGittermWorkspaceClient() uses the identity Gitterm injects (WORKSPACE_API_URL, WORKSPACE_AUTH_TOKEN, WORKSPACE_ID). It can only see that workspace.
See Previews.
Sandboxes
@gitterm/sdk/sandbox runs commands in a plain remote sandbox, with no agent inside, for a local agent or script. An account with apiKey or apiKeyEnv uses your own provider key; one without goes through your Gitterm plan ({ size: "medium" }, or serverUrl and provider for a self-hosted server). For the model, limits and accounts, see Sandboxes.
import {
captureSource,
createSandboxBackend,
createSandboxClient,
resolveSandboxAccount,
} from "@gitterm/sdk/sandbox";
const account = await resolveSandboxAccount({ provider: "e2b", apiKeyEnv: "E2B_API_KEY" });
const sandboxes = createSandboxClient({ backend: createSandboxBackend(account) });
const sandbox = await sandboxes.create({ name: "ci", lifetimeMinutes: 30 });
await sandboxes.sync(sandbox, await captureSource(process.cwd()));
const job = await sandboxes.exec(sandbox, { command: "npm test", waitMs: 300_000 });
console.log(job.state.status, job.log.text);
await sandboxes.destroy(sandbox);Methods: create, list, get, sync, exec, job, wait, cancel, preview, fetchArtifact, applyArtifact, resume, destroy.
preview() returns url, the URL to open, with public, ready, and viaGitterm (true when Gitterm serves url; a private one opens for the owner signed in to Gitterm). access holds the direct provider url and headers for a local proxy such as startPreviewProxy. The headers are secret: keep them away from models, logs, and storage.
The Gitterm sandbox calls are also on the main client: client.sandboxes.providers, create, list, get, run, writeFile, readFile, preview, resume and terminate.
Direct provider mode
@gitterm/sdk/direct runs the same workspace and run API against your own provider account, with no Gitterm server. Your app owns state, cleanup and credentials. It supports E2B, Daytona, Vercel, Boat, exe.dev and Railway.
import { createDirectGittermClient } from "@gitterm/sdk/direct";
const direct = createDirectGittermClient({
provider: { type: "e2b", apiKey: process.env.E2B_API_KEY!, size: "standard" },
});Direct mode accepts repositoryCredentials but not connections, and only inline model credentials ({ source: "apiKey" }).
Errors
Every API error is a GittermError with a stable code. Match on the code, not the message.
import { GittermError, WorkspaceLifecycleError } from "@gitterm/sdk";
try {
await client.runs.create({ workspace: workspaceId, prompt });
} catch (error) {
if (error instanceof WorkspaceLifecycleError && error.code === "WORKSPACE_NOT_RUNNING") {
await client.workspaces.ensureRunning(workspaceId);
} else if (error instanceof GittermError && error.code === "TIMEOUT") {
// the wait ended; the run may still be going
} else throw error;
}GittermError.status holds the HTTP status when the server answered. TIMEOUT and ABORTED stop the wait, not the agent; call runs.cancel() to stop the agent.