Skip to main content
Snapshots and fork let you capture and duplicate box state. Use snapshots to save a point-in-time checkpoint you can restore later, and fork to instantly clone a running box.

API

Create a snapshot

Call snapshot() on a running or paused box. The returned Snapshot object contains the ID you need to restore later.
const snapshot = await box.snapshot({ name: "after-setup" })

console.log(snapshot)
// {
//   id: "snap_x7f...",
//   name: "after-setup",
//   boxId: "box_abc123",
//   sizeBytes: 52428800,
//   createdAt: "2026-02-23T..."
// }
Snapshots are independent of the source box. Deleting the original box does not delete its snapshots. They remain available for restore at any time.

List snapshots

Retrieve all snapshots belonging to a box.
const snapshots = await box.listSnapshots()

console.log(snapshots)
// [
//   { id: "snap_x7f...", name: "after-setup", status: "ready", ... },
//   { id: "snap_a3b...", name: "pre-refactor", status: "ready", ... },
// ]

Delete a snapshot

Remove a snapshot by ID.
await box.deleteSnapshot("snap_x7f...")

Restore from a snapshot

Box.fromSnapshot() provisions a new box with the exact state from the snapshot. The original box is unaffected. You can branch into multiple boxes from the same checkpoint.
const restored = await Box.fromSnapshot(snapshot.id)

const files = await restored.files.list("/work")
console.log(files)

Fork

fork() creates a new box with the same workspace state as the current box. Unlike snapshots, forking does not create a persistent checkpoint — it directly clones the live state into a new box.
const forked = await box.fork()

// The forked box has the same files, packages, and environment
const files = await forked.files.list("/work")
The original box is unaffected by a fork. Both boxes continue to run independently.

Examples

Checkpoint before risky operations

Snapshot your working state, then let the agent attempt a large refactor. If the result is bad, we restore the original state and try a different prompt.
import { Box, ClaudeCode } from "@upstash/box"

const box = await Box.create({
  runtime: "node",
  agent: { model: ClaudeCode.Opus_4_6, apiKey: process.env.ANTHROPIC_API_KEY },
  git: { token: process.env.GITHUB_TOKEN },
})

await box.git.clone({ repo: "github.com/my-org/my-repo" })
const checkpoint = await box.snapshot({ name: "pre-refactor" })

const run = await box.agent.run({
  // 👇 Potentially difficult task
  prompt: "Rewrite the database layer to use connection pooling",
})

if (run.status === "failed") {
  const fallback = await Box.fromSnapshot(checkpoint.id)
  await fallback.agent.run({
    prompt: "Add connection pooling to the existing database layer without rewriting it",
  })
}

Reusable base environments

Install dependencies once, snapshot, then spawn boxes from the snapshot to skip setup time.
import { Box } from "@upstash/box"

const base = await Box.create({ runtime: "node" })
await base.exec("npm install -g typescript eslint prettier")
const baseSnap = await base.snapshot({ name: "node-toolchain" })
await base.delete()

const boxes = await Promise.all(
  tasks.map(async (task) => {
    const box = await Box.fromSnapshot(baseSnap.id)
    await box.agent.run({ prompt: task })
    return box
  })
)

Fork for parallel exploration

Fork a box to try multiple approaches on the same codebase without creating a snapshot first.
import { Box, ClaudeCode } from "@upstash/box"

const box = await Box.create({
  runtime: "node",
  agent: { model: ClaudeCode.Sonnet_4_6, apiKey: process.env.ANTHROPIC_API_KEY },
  git: { token: process.env.GITHUB_TOKEN },
})

await box.git.clone({ repo: "github.com/my-org/my-repo" })

const [approach1, approach2] = await Promise.all([box.fork(), box.fork()])

await Promise.all([
  approach1.agent.run({ prompt: "Fix the bug by refactoring the parser" }),
  approach2.agent.run({ prompt: "Fix the bug by adding input validation" }),
])

Fan-out from a single state

Clone a repo once, snapshot, then run different agents or prompts in parallel from the same starting point.
import { Box } from "@upstash/box"

const seed = await Box.create({
  runtime: "node",
  git: { token: process.env.GITHUB_TOKEN },
})
await seed.git.clone({ repo: "github.com/your-org/monorepo" })
const snap = await seed.snapshot({ name: "repo-cloned" })
await seed.delete()

const [security, performance, docs] = await Promise.all([
  Box.fromSnapshot(snap.id),
  Box.fromSnapshot(snap.id),
  Box.fromSnapshot(snap.id),
])

await Promise.all([
  security.agent.run({ prompt: "Audit src/ for SQL injection vulnerabilities" }),
  performance.agent.run({ prompt: "Profile the hot paths in src/api/ and optimize" }),
  docs.agent.run({ prompt: "Generate API documentation for all public exports" }),
])