---
title: TypeScript SDK
description: The @ibby-ai/sdk package for Node 20 and later. Create sandboxes, write and read files, run commands, and check jobs.
---

```sh
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

```ts
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.

```ts
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](#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](/concepts#a-read-only-store-folder) 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.

```ts
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

```ts
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`.

```ts
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:

```ts
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](/concepts#images) 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.

```ts
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

```ts
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);
}
```
