Skip to main content
For every box, you can configure a built-in agent like Claude Code, Codex or OpenCode. It has access to the filesystem, git, and shell commands and should simulate running an Agent on your own computer. You can choose between:
  • run() when you want to wait for completion and then read the final typed result.
  • stream() when you want real-time output while the agent is running.

Configure an Agent

Get your Claude API key from the Claude Console.
.env
UPSTASH_BOX_API_KEY=abx_xxxxxxxxxxxxxxxxxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxx
import { Box, Runtime, ClaudeCode } from "@upstash/box"

const box = await Box.create({
  runtime: "node",
  agent: {
    model: ClaudeCode.Sonnet_4_5,
    apiKey: process.env.ANTHROPIC_API_KEY!,
  },
})

Quickstart

Run an agent

Use run() when you only need the final result.
const run = await box.agent.run({
  prompt: "Analyze /work/report.csv and return top 10 customers by revenue",
})

console.log(run.status)
console.log(run.result)
console.log(run.cost.totalUsd)

Stream an agent

Use stream() when you want to display progress in real time.
const stream = await box.agent.stream({
  prompt: "Refactor the payment module",
})

for await (const chunk of stream) {
  if (chunk.type === "text-delta") process.stdout.write(chunk.text)
}

console.log(stream.status)
console.log(stream.result)
console.log(stream.cost.totalUsd)

API

Prompt (required)

Type: string
Supported on: box.agent.run() and box.agent.stream()
The task instruction sent to the agent.

Timeout

Type: number
Supported on: box.agent.run() and box.agent.stream()
Default: no execution timeout
Execution timeout in milliseconds. When reached, the run is aborted.

onToolUse

Type: { name: string; input: Record<string, unknown> }
Supported on: box.agent.run() and box.agent.stream()
Called whenever the agent invokes a tool (for example file, shell, or git tools).

responseSchema

Type: Zod Schema
Supported on: box.agent.run()
Attach a Zod schema to get typed output.
import { z } from "zod"

const responseSchema = z.object({
  customers: z.array(
    z.object({
      name: z.string(),
      revenue: z.number(),
    }),
  ),
})

const analysis = await box.agent.run({
  prompt: "Analyze /work/report.csv and return top customers by revenue",
  responseSchema,
})

console.log(analysis.result.customers)

maxRetries

Type: number
Supported on: box.agent.run()
Default: 0
Retry count to compensate temporary provider outages or similar transient errors. Retries use exponential backoff (1s, 2s, 4s, …) capped at 30s.

Webhook

Type: WebhookConfig
Supported on: box.agent.run()
Useful for fire-and-forget mode. The SDK returns immediately and sends the completion payload to your webhook URL when the run succeeds or fails.

Examples

Minimal example

Clone a repository, run the agent, and open a pull request.
import { Box, Runtime, ClaudeCode } from "@upstash/box"

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

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

const stream = await box.agent.stream({
  prompt: "Fix the null token bug in src/auth.ts and add tests",
})

for await (const chunk of stream) {
  if (chunk.type === "text-delta") process.stdout.write(chunk.text)
}

await box.git.createPR({
  title: "Fix null token bug",
  base: "main",
})

Document processing agent

Run one box per file, process in parallel, and return ranked structured results.
import { Box, Runtime, ClaudeCode } from "@upstash/box"
import { readdir } from "fs/promises"
import { z } from "zod"

const responseSchema = z.object({
  name: z.string(),
  email: z.string(),
  yearsOfExperience: z.number(),
  skills: z.array(z.string()),
  score: z.number().min(0).max(100),
  summary: z.string(),
})

const job = "Senior Backend Engineer (Node.js, PostgreSQL)."

const files = await readdir("./resumes")
const resumes = files.filter((file) => file.endsWith(".pdf"))

const results = await Promise.all(
  resumes.map(async (file) => {
    const box = await Box.create({
      runtime: "node",
      agent: {
        model: ClaudeCode.Opus_4_5,
        apiKey: process.env.ANTHROPIC_API_KEY,
      },
    })

    await box.files.upload([
      { path: `./resumes/${file}`, destination: "/work/resume.pdf" },
    ])

    const run = await box.agent.run({
      prompt: `Read /work/resume.pdf. Extract candidate data and score 0-100 for: ${job}`,
      responseSchema,
    })

    await box.delete()

    return { file, ...run.result, cost: run.cost.totalUsd }
  }),
)

const ranked = results.sort((a, b) => b.score - a.score)