TypeScript SDK
The @ibby-ai/sdk package for Node 20 and later. Create sandboxes, write and read files, run commands, and check jobs.
npm install @ibby-ai/sdk
The package is an ES module for Node 20 or later, with no dependencies of its own. Request and response fields use the API’s own names, such as workdir, timeout_ms and exit_code.
Make a client
import { Ibby } from "@ibby-ai/sdk";
const ibby = new Ibby({
apiKey: process.env["IBBY_API_KEY"] ?? "",
// Optional, shown with their defaults:
baseUrl: "https://api.ibby.ai",
maxRetries: 2,
timeoutMs: 150_000,
});
maxRetries is how many times a call is retried after a dropped connection or a 503. Set it to 0 to turn retries off. A command is never retried on its own. timeoutMs is the most one attempt may take; null turns it off.
The key is read once. Surrounding spaces and line breaks are ignored; a key with a space or a control character inside makes the constructor throw, without showing the key.
Sandboxes
create() returns a Sandbox. Its methods act on that sandbox.
import { Ibby } from "@ibby-ai/sdk";
const ibby = new Ibby({ apiKey: process.env["IBBY_API_KEY"] ?? "" });
const sandbox = await ibby.create({
workdir: "/work",
name: "report-run-1",
lifetime_ms: 60_000,
resources: { cpu: 0.5, memory_mib: 512 },
});
console.log(sandbox.id, sandbox.info.status, sandbox.info.resources);
await sandbox.makeDirectories(["/work/in", "/work/out"]);
await sandbox.writeFiles({
"/work/in/data.csv": "a,b\n1,2\n",
"/work/in/logo.bin": new Uint8Array([0, 1, 2]),
});
const listing = await sandbox.listFiles("/work/in");
for (const entry of listing.entries) {
console.log(entry.type, entry.size_bytes, entry.path);
}
const bytes = await sandbox.readFile("/work/in/data.csv", { maxBytes: 1024 });
console.log(new TextDecoder().decode(bytes));
await sandbox.remove();
create() also takes image to run on an image you built (see Images), and store: { mount_path: "/work/in", key_prefix: "runs/1/" } to attach part of the lane’s store folder read-only; the concepts page gives the rules. A rule between two fields, such as store.mount_path sitting inside workdir, is checked by the API, which answers 400 invalid_request.
To act on a sandbox you made earlier, use ibby.sandbox(id). It makes no network call; its info is null until you call refresh(). ibby.get(id) fetches the record as well. To remove a sandbox by its name after a crash, when you no longer have its id, use ibby.removeByName(name). An unknown name is fine.
import { Ibby } from "@ibby-ai/sdk";
const ibby = new Ibby({ apiKey: process.env["IBBY_API_KEY"] ?? "" });
const sandbox = ibby.sandbox("sbx_...");
const info = await sandbox.refresh();
console.log(info.status);
await ibby.removeByName("report-run-1");
Running commands
import { Ibby } from "@ibby-ai/sdk";
const ibby = new Ibby({ apiKey: process.env["IBBY_API_KEY"] ?? "" });
const sandbox = await ibby.create({ workdir: "/work" });
const result = await sandbox.exec({
command: ["sh", "-c", "echo $GREETING"],
cwd: "/work",
env: { GREETING: "hello" },
timeout_ms: 30_000,
});
if (result.outcome === "timed_out") {
console.log("timed out");
} else {
console.log(result.exit_code, result.stdout, result.stdout_truncated);
}
A timeout is a result (outcome: "timed_out"), not an error. stdout is null when the output is unavailable: when you repeat a finished command with the same idempotencyKey (the outcome comes back, the output does not), or, in this version, when the command timed out. stdout_truncated is false whenever stdout is null.
Errors
Every failed call throws an IbbyError.
import { Ibby, IbbyError } from "@ibby-ai/sdk";
const ibby = new Ibby({ apiKey: process.env["IBBY_API_KEY"] ?? "" });
try {
await ibby.get("sbx_missing");
} catch (error) {
if (error instanceof IbbyError) {
console.log(error.status, error.code, error.message, error.requestId);
}
}
code is the API’s stable code, such as not_found or sandbox_stopped, and message is the API’s own sentence. When no answer came back at all, status is 0 and code is connection_error or timeout. A redirect is never followed: it is an IbbyError with code unexpected_response.
Safe retries
Calls that create or write send a fresh Idempotency-Key. To retry one yourself, for example after your process restarts, pass your own key both times:
import { Ibby } from "@ibby-ai/sdk";
const ibby = new Ibby({ apiKey: process.env["IBBY_API_KEY"] ?? "" });
const sandbox = await ibby.create(
{ workdir: "/work" },
{ idempotencyKey: "nightly-2026-11-13" }
);
console.log(sandbox.id);
Images
buildImage() starts a build from a recipe and returns its job. When the job is done, its result names the image to create sandboxes from. The concepts page explains recipes. getImage() throws an IbbyError with code image_not_built (status 409) when the image has no build on the lane’s current provider, which happens only after a lane changes provider; rebuild it from its recipe.
import { Ibby } from "@ibby-ai/sdk";
import type { ImageRecipe } from "@ibby-ai/sdk";
const ibby = new Ibby({ apiKey: process.env["IBBY_API_KEY"] ?? "" });
const hash =
"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
const recipe: ImageRecipe = {
base: { reference: "python:3.13-slim", digest: hash },
packages: [{ name: "numpy", version: "2.3.1", hashes: [hash] }],
import_check: ["numpy"],
};
let job = await ibby.buildImage(recipe);
while (job.status === "accepted" || job.status === "running") {
await new Promise((resolve) => setTimeout(resolve, 5_000));
job = await ibby.getJob(job.id);
}
const imageId = job.result?.["image_id"];
if (job.status === "done" && typeof imageId === "string") {
const image = await ibby.getImage(imageId);
console.log(image.status);
const sandbox = await ibby.create({ workdir: "/work", image: imageId });
await sandbox.remove();
}
Jobs
import { Ibby } from "@ibby-ai/sdk";
const ibby = new Ibby({ apiKey: process.env["IBBY_API_KEY"] ?? "" });
const job = await ibby.getJob("job_...");
if (job.status === "running") {
await ibby.cancelJob(job.id);
}