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" }),
])