SDK reference
The core surface of @tangle-network/sandbox. The npm package documents every option; this page covers what most builders reach for.
Each TypeScript block is a separate program for Node.js 22 or later.
Set TANGLE_API_KEY before running it; optional SANDBOX_BASE_URL selects another deployment.
Examples that create machines or run agents consume account credit.
Creation examples release their machines in finally.
Save one block as example.ts and run it with npx tsx example.ts.
npm install @tangle-network/sandbox
npm install --save-dev tsxClient
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
timeoutMs: 30000,
});client.create(options?)
Create a sandbox and get back a SandboxInstance. Common options:
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const box = await client.create({
name: "my-project",
environment: "universal",
backend: { type: "opencode" }, // Also supports Claude Code, Codex, and other supported harnesses.
env: { NODE_ENV: "development" },
resources: { cpuCores: 2, memoryMB: 4096, diskGB: 20 },
maxLifetimeSeconds: 3600,
idleTimeoutSeconds: 900,
});
try {
console.log(box.id);
} finally {
await box.delete();
}environment accepts a named environment from client.environments.list(), a container image reference, or an SDK-built image ID.
Omit it to use the server default.
To restore a snapshot, provide both fromSnapshot and its owning fromSandboxId when creating the sandbox.
backend.type picks the coding harness. OpenCode is the default; Claude Code, Codex, and the other supported harnesses are selected the same way.
client.list(options?) · client.get(id) · client.usage()
Set TANGLE_SANDBOX_ID to an existing sandbox you own.
This example reads that machine and your account usage without creating a machine.
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const sandboxId = process.env.TANGLE_SANDBOX_ID;
if (!sandboxId) throw new Error("Set TANGLE_SANDBOX_ID to an existing sandbox ID");
const running = await client.list({ status: "running", limit: 10 });
const box = await client.get(sandboxId);
if (!box) throw new Error("Sandbox not found");
const usage = await client.usage();
console.log(running, box.id, usage);client.runBatch(request, options?)
Run one-shot tasks across freshly provisioned sandboxes in parallel. For coordinated multi-machine work with shared workspaces and policy caps, use fleets instead.
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const jobId = process.env.TANGLE_BATCH_JOB_ID;
if (!jobId) throw new Error("Set TANGLE_BATCH_JOB_ID to your saved job ID");
const result = await client.runBatch(
{
tasks: [
{ id: "task-1", message: "Create a JavaScript function that adds two numbers and test it." },
{ id: "task-2", message: "Create a JavaScript function that reverses a string and test it." },
],
backends: [{ id: "worker", type: "opencode" }],
},
{ idempotencyKey: jobId },
);
console.log(result.totalSuccess, result.totalFailure);Select batch workers from the supported harnesses.
Reuse an idempotencyKey only when retrying the same batch with the same request body.
A keyed batch continues after a client disconnect.
While its run record remains retained, a retry joins active work or replays its completed result.
options.signal cancels the client stream.
Without an idempotency key, cancellation or disconnection also stops server work.
Sandbox instance
box.exec(command, options?)
Run a shell command.
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const box = await client.create({ environment: "universal" });
try {
const result = await box.exec("node --version", {
cwd: "/workspace",
env: { CI: "true" },
timeoutMs: 60000,
});
console.log(result.exitCode, result.stdout);
} finally {
await box.delete();
}box.prompt(message, options?) · box.streamPrompt(message, options?)
Run one agent turn. prompt returns the result; streamPrompt yields events as they happen.
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const box = await client.create({ environment: "universal" });
try {
const result = await box.prompt("Create a JavaScript function that adds two numbers.");
console.log(result);
for await (const event of box.streamPrompt("Add tests for that function and run them.")) {
console.log(event);
}
} finally {
await box.delete();
}box.createTaskSession(options) · box.taskSession(id)
Create a background task session with isolated file changes using one of the supported harnesses.
Set TANGLE_REPO_URL to a public Git repository URL.
The example clones that repository before creating the task session.
import { randomUUID } from "node:crypto";
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const repoUrl = process.env.TANGLE_REPO_URL;
if (!repoUrl) throw new Error("Set TANGLE_REPO_URL to a public Git repository URL");
const box = await client.create({
environment: "universal",
git: { url: repoUrl },
});
try {
const { session: task } = await box.createTaskSession({
sessionId: randomUUID(),
title: "Write a README",
backend: { type: "opencode" }, // Also supports Claude Code, Codex, and other supported harnesses.
isolateFileWrites: true,
});
await task.sendMessage({
parts: [{ type: "text", text: "Write a README explaining this workspace." }],
turnId: randomUUID(),
});
console.log(await task.result());
console.log(await task.changes());
} finally {
await box.delete();
}This example clones your repository, inspects isolated changes, then deletes its machine.
For retained tasks, store the sandbox ID and task session ID instead of deleting the machine.
Reconnect with client.get(savedSandboxId) and box.taskSession(savedSessionId).
Review task.changes() before applying changes with task.commit().
Durable sessions
Use dispatchPrompt for a turn you must reconnect to after a client crash, redeploy, or browser reload.
sessionId identifies the conversation; turnId identifies one logical turn.
Derive both IDs from your trusted job record and store them before dispatching.
Reuse both IDs and the same prompt when retrying that job.
Set TANGLE_SANDBOX_ID, TANGLE_SESSION_ID, and TANGLE_TURN_ID from that saved record.
This example retains the existing sandbox so another process can reconnect.
Delete it when the job and any retries finish.
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const sandboxId = process.env.TANGLE_SANDBOX_ID;
const sessionId = process.env.TANGLE_SESSION_ID;
const turnId = process.env.TANGLE_TURN_ID;
if (!sandboxId || !sessionId || !turnId) {
throw new Error("Set TANGLE_SANDBOX_ID, TANGLE_SESSION_ID, and TANGLE_TURN_ID from your saved job record");
}
const box = await client.get(sandboxId);
if (!box) throw new Error("Sandbox not found");
const receipt = await box.dispatchPrompt("Analyze code quality", {
sessionId,
turnId,
});
console.log(receipt);
const cached = await box.findCompletedTurn(turnId, {
sessionId: receipt.sessionId,
});
if (cached) {
console.log(cached.result);
} else {
if (!receipt.executionId) throw new Error("Missing execution ID");
const final = await box.session(receipt.sessionId).result({
executionId: receipt.executionId,
});
console.log(final);
}Store the sandbox ID and dispatch receipt to reconnect from another process.
Use client.get(sandboxId) to obtain the sandbox, then select the receipt’s session and execution IDs.
Use session.events({ executionId }) to follow output while that execution runs.
The same sessionId prevents a second dispatch while its session is in flight.
After completion, that session can receive a new turn.
Pass a stable turnId to deduplicate retries while the completed result remains cached.
The completed-turn cache can outlive its session; use findCompletedTurn to retrieve that cached result.
dispatched: false means the SDK found prior work and did not dispatch another turn.
alreadyExisted reports whether the session existed, not whether work ran.
GPU leases
Keep the base sandbox cheap and attach a GPU only around the step that needs it. Every lease takes a hard spend cap and lifetime.
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const box = await client.create({ environment: "universal" });
try {
const lease = await box.gpu.attach({
accelerator: { kind: "nvidia-h100", count: 1 },
maxSpendUsd: 5,
maxLifetimeSeconds: 600,
});
try {
console.log(await box.gpu.exec(lease.id, { command: "nvidia-smi" }));
} finally {
await box.gpu.detach(lease.id);
}
} finally {
await box.delete();
}Lifecycle
import { Sandbox } from "@tangle-network/sandbox";
const apiKey = process.env.TANGLE_API_KEY;
if (!apiKey) throw new Error("Set TANGLE_API_KEY");
const client = new Sandbox({
apiKey,
baseUrl: process.env.SANDBOX_BASE_URL ?? "https://sandbox.tangle.tools",
});
const box = await client.create({ environment: "universal" });
try {
await box.stop();
await box.resume();
console.log((await box.exec("node --version")).stdout);
} finally {
await box.delete();
}Next
@tangle-network/sandboxon npm for fleets, snapshots, BYOS3, scoped browser tokens, and every option.- Connect to Intelligence to trace each run and see why an agent failed.