# Snapshots

Snapshots let you capture and duplicate box state. Use **snapshots** to save a point-in-time checkpoint you can restore later.

---

## What's included

A snapshot captures:

- **Filesystem**: the full disk state of the box at the time of the snapshot.
- **Agent configuration**: the agent harness, model, and API key settings.

A snapshot does not carry active schedules over to the new box.

<Note>
Snapshots are the best way to preserve cloned repositories, installed dependencies, and prepared workspaces. If your box already has a repo checked out, restoring from a snapshot is usually faster than cloning it again.
</Note>

---

## API

### Create a snapshot

Call `snapshot()` on a running or paused box. The returned `Snapshot` object contains the ID you need to restore later.

<CodeGroup>
```typescript box.ts
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..."
// }
```

```python box.py
snapshot = box.snapshot(name="after-setup")

print(snapshot)
# Snapshot(
#   id="snap_x7f...",
#   name="after-setup",
#   box_id="box_abc123",
#   size_bytes=52428800,
#   status="ready",
#   created_at=1771845600,
# )
```
</CodeGroup>

<Note>
Snapshots are independent of the source box. Deleting the original box does not delete its snapshots. They remain available for restore at any time.
</Note>

---

### List snapshots

Retrieve all snapshots belonging to a box.

<CodeGroup>
```typescript box.ts
const snapshots = await box.listSnapshots()

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

```python box.py
snapshots = box.list_snapshots()

print(snapshots)
# [
#   Snapshot(id="snap_x7f...", name="after-setup", status="ready", ...),
#   Snapshot(id="snap_a3b...", name="pre-refactor", status="ready", ...),
# ]
```
</CodeGroup>

---

### Delete a snapshot

Remove a snapshot by ID.

<CodeGroup>
```typescript box.ts
await box.deleteSnapshot("snap_x7f...")
```

```python box.py
box.delete_snapshot("snap_x7f...")
```
</CodeGroup>

---

### 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.

<CodeGroup>
```typescript box.ts
const restored = await Box.fromSnapshot(snapshot.id)

// Box.fromSnapshot accepts creation options such as name:
const named = await Box.fromSnapshot(snapshot.id, { name: "restored-worker" })

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

```python box.py
restored = Box.from_snapshot(snapshot.id)

# Box.from_snapshot accepts creation options such as name:
named = Box.from_snapshot(snapshot.id, name="restored-worker")

files = restored.files.list("/work")
print(files)
```
</CodeGroup>

The restored box starts with the same workspace state from the snapshot. You can also configure lifecycle settings such as keep-alive and init commands on the restored box.

---

## 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.

<CodeGroup>
```typescript box.ts
import { Agent, Box } from "@upstash/box"

const box = await Box.create({
  runtime: "node",
  agent: { harness: Agent.ClaudeCode, model: "anthropic/claude-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",
  })
}
```

```python box.py
import os
from upstash_box import Box, Agent

box = Box.create(
    runtime="node",
    agent={"harness": Agent.CLAUDE_CODE, "model": "anthropic/claude-opus-4-6", "api_key": os.environ["ANTHROPIC_API_KEY"]},
    git={"token": os.environ["GITHUB_TOKEN"]},
)

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

run = box.agent.run(
    # 👇 Potentially difficult task
    prompt="Rewrite the database layer to use connection pooling",
)

if run.status == "failed":
    fallback = Box.from_snapshot(checkpoint.id)
    fallback.agent.run(
        prompt="Add connection pooling to the existing database layer without rewriting it",
    )
```
</CodeGroup>

### Reusable base environments

Install dependencies once, snapshot, then spawn boxes from the snapshot to skip setup time.

<CodeGroup>
```typescript box.ts
import { Agent, Box } from "@upstash/box"

const base = await Box.create({ runtime: "node" })
await base.exec.command("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
  })
)
```

```python box.py
import asyncio
from upstash_box import AsyncBox

async def main() -> None:
    base = await AsyncBox.create(runtime="node")
    await base.exec.command("npm install -g typescript eslint prettier")
    base_snap = await base.snapshot(name="node-toolchain")
    await base.delete()

    async def run_task(task: str) -> AsyncBox:
        box = await AsyncBox.from_snapshot(base_snap.id)
        await box.agent.run(prompt=task)
        return box

    # spawn boxes from the snapshot in parallel
    await asyncio.gather(*(run_task(t) for t in tasks))

asyncio.run(main())
```
</CodeGroup>

### Fan-out from a single state

Clone a repo once, snapshot, then run different agents or prompts in parallel from the same starting point.

<CodeGroup>
```typescript box.ts
import { Agent, 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" }),
])
```

```python box.py
import asyncio
import os
from upstash_box import AsyncBox

async def main() -> None:
    seed = await AsyncBox.create(
        runtime="node",
        git={"token": os.environ["GITHUB_TOKEN"]},
    )
    await seed.git.clone(repo="github.com/your-org/monorepo")
    snap = await seed.snapshot(name="repo-cloned")
    await seed.delete()

    security, performance, docs = await asyncio.gather(
        AsyncBox.from_snapshot(snap.id),
        AsyncBox.from_snapshot(snap.id),
        AsyncBox.from_snapshot(snap.id),
    )

    await asyncio.gather(
        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"),
    )

asyncio.run(main())
```
</CodeGroup>
