---
title: Run a research workspace on Vercel
description: Plan a durable research workspace on Vercel's Agentic Infrastructure with Workflows, internal API tools, Vercel Sandbox isolation, artifacts, and tradeoffs.
url: "https://vercel.com/kb/guide/run-research-workspace-vercel"
published: 2026-09-22
last_updated: 2026-09-22
authors: Vercel
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

Research workspaces on Vercel are durable agent applications where Workflows run the multi-step control plane, Vercel Functions expose trusted API handlers, Vercel Sandbox runs untrusted code or tool processes in isolated microVMs, and Vercel Blob stores artifacts that outlive each run. Platform teams often default to Kubernetes with LangGraph or Temporal for orchestration and E2B, Modal, or Daytona as a separate sandbox plane, because that shape is familiar from other workloads. For a web-native workspace with internal API tools and isolated code or browser execution, Vercel covers the same responsibilities without a separate orchestration cluster or sandbox fleet to operate.

This guide turns the [multi-step research agent](https://vercel.com/kb/guide/how-to-run-a-multi-step-research-agent-on-vercel) into a production workspace architecture and a platform decision, with the tradeoffs stated plainly.

## Overview

In this guide, you'll learn how to:

- Map the workspace UI, agent loop, internal tools, sandboxes, and artifacts to Vercel products
  
- Run long research jobs as durable workflows with step budgets, approvals, and cancellation handling
  
- Expose internal APIs as narrow, typed tools that run in trusted backend code with scoped credentials
  
- Isolate model-written code and browser automation with Vercel Sandbox network policies
  
- Store reports, evidence, and audit records outside the run
  
- Decide when Vercel should be the primary platform and when to keep a separate sandbox plane or a self-hosted orchestrator
  

## Prerequisites

Before you start, you need:

- Your [Vercel account](https://vercel.com/signup) and a linked project on a Pro or Enterprise plan.
  
- The implementation from [How to run a multi-step research agent on Vercel](https://vercel.com/kb/guide/how-to-run-a-multi-step-research-agent-on-vercel). This guide extends that build without repeating it.
  
- [Node.js](https://nodejs.org/) 22 or later, which the AI SDK 7 and Workflow SDK require.
  
- An internal API that the workspace should call, or a mock gateway for testing.
  
- Storage for artifacts in a private [Vercel Blob](https://vercel.com/docs/vercel-blob) store.
  
- Optionally, [Static IPs](https://vercel.com/docs/connectivity/static-ips) or [Secure Compute](https://vercel.com/docs/connectivity/secure-compute) if your internal APIs sit behind an IP allowlist or on a private network.
  

## How it works

The workspace separates work by trust level. Each zone has one job and one set of credentials.

| Zone                 | What runs there                                                             | Trust level | Vercel product                                                                      |
| -------------------- | --------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------- |
| Workspace UI and API | Next.js app, run workflow and status routes, artifact downloads             | Trusted     | Vercel Functions                                                                    |
| Agent control plane  | The research loop, planning, tool selection, approvals                      | Trusted     | Workflows with `WorkflowAgent`                                                      |
| Internal tools       | Typed calls to internal APIs with scoped credentials                        | Trusted     | Workflow steps in Vercel Functions, optionally through Secure Compute or Static IPs |
| Model access         | Model routing, fallbacks, spend limits                                      | Trusted     | AI Gateway                                                                          |
| Untrusted execution  | Model-written code, file parsing, repository inspection, browser automation | Untrusted   | Vercel Sandbox, or a Marketplace browser service                                    |
| Artifacts and audit  | Reports, evidence files, tool-call logs, approval records                   | Trusted     | Vercel Blob plus your application database                                          |

Three rules hold the architecture together. Each one stands on its own:

- **The agent control plane stays outside the sandbox.** The workspace calls internal APIs through narrow typed tools in trusted backend code, and it creates per-run or per-task sandboxes only for untrusted work such as model-written code, file inspection, browser automation, or user-generated commands. Sandboxed code receives the inputs it needs and returns outputs. It never receives a broad route into production systems.
  
- **Durability lives in workflow checkpoints, not in a process.** Workflows records every completed step of a run, so a research job that runs for hours resumes after a crash or a deployment instead of starting over. Artifacts live in durable storage rather than inside the transient process or the sandbox filesystem.
  
- **Vercel replaces the sandbox fleet, not every backend.** Vercel removes the need to operate a Kubernetes sandbox tier and a separate orchestrator for web-native agent workspaces. Teams with long-lived stateful workers, custom VPC-only microVM fleets, or non-HTTP always-on services may still keep those components outside Vercel and call them from workflow steps.
  

### Use cases this architecture covers

- **Internal research**: agents that join customer, billing, and support data from internal APIs into a briefing
  
- **Repository analysis**: cloning a repository into a sandbox, running static analysis, and returning findings
  
- **Security review**: executing untrusted scripts or dependency audits with no network access
  
- **Data extraction**: parsing uploaded files or datasets in a sandbox and writing structured results to Vercel Blob
  
- **Browser research**: reading dynamic sites, signing into dashboards, or filling forms through a sandboxed browser
  
- **Evidence gathering**: producing a report with citations, source snapshots, and an auditable tool-call trail
  

## Choose the Vercel control plane for the workspace

### Keep the agent loop outside the sandbox

The agent loop decides what to do next, and the sandbox executes one bounded task. Keeping them apart means the loop can outlive any single sandbox, retry a failed sandbox step, and hold credentials that the sandbox never sees. When the loop runs inside the VM, a sandbox timeout or crash kills the entire run, and every credential the loop uses becomes accessible to model-written code on the same filesystem.

In practice, the loop is a workflow function, and each sandbox action is a step inside it. The step creates or reattaches to a sandbox, writes inputs, runs a command, reads outputs, and returns them to the loop. If the step fails, Vercel Workflows retries it and `Sandbox.getOrCreate()` reattaches to the same sandbox instead of provisioning another one.

### Map workspace components to Vercel products

| Component                                  | Vercel product                                                                                          | Why it belongs there                                                                     |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Workspace UI, run start and status routes  | Next.js on Vercel Functions                                                                             | Request-driven, authenticated, scales with users                                         |
| Multi-step agent loop                      | Vercel Workflows with `WorkflowAgent` from `@ai-sdk/workflow`                                           | Checkpointed steps, retries, approvals, resumable streams, no duration limit             |
| Internal API tools                         | Workflow steps marked `'use step'`                                                                      | Runs in trusted code with project environment variables and OIDC                         |
| Model calls                                | Vercel AI Gateway                                                                                       | Model strings like `anthropic/claude-fable-5`, fallbacks, budgets, per-call cost logging |
| Model-written code, file parsing, commands | Vercel Sandbox                                                                                          | Firecracker microVM per sandbox with its own filesystem and an egress firewall           |
| Browser automation                         | Browserbase or Kernel from the Vercel Marketplace, or Chromium with agent-browser inside Vercel Sandbox | Long-lived browser sessions and anti-bot tooling, or full isolation in your own VM       |
| Reports, evidence, downloads               | Vercel Blob (private store)                                                                             | Durable object storage with authenticated reads and signed URLs                          |
| Run metadata, audit events, permissions    | Your application database                                                                               | Relational queries for who ran what, with which tools, under which approval              |
| Custom tool runtimes                       | Vercel Container Registry images on Vercel Functions or Vercel Sandbox                                  | Ship OCI images for tooling the managed images don't include                             |

Vercel supports OCI container images on Vercel Functions, stores them in [Vercel Container Registry](https://vercel.com/docs/container-registry), and boots Vercel Sandbox from custom images. What Vercel does not provide is an always-on stateful container host. Model each component as request-driven Functions, durable Workflows, or on-demand Sandboxes, and keep continuously running services elsewhere.

### Decide which state belongs in the database, artifacts, or workflow checkpoints

| State                                              | Where it lives                                           | Reason                                                                           |
| -------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Loop state (messages, tool results, step outputs)  | Workflow checkpoints                                     | Managed persistence, replayed automatically on resume                            |
| Run record (owner, status, started at, budget)     | Application database                                     | Queried by the UI and by audit tooling                                           |
| User-facing artifacts (reports, CSVs, screenshots) | Vercel Blob                                              | Durable, downloadable, access-controlled                                         |
| Evidence files (fetched pages, raw datasets)       | Vercel Blob                                              | Retained for citation checks after the run ends                                  |
| Audit events (tool calls, approvals, sandbox IDs)  | Application database, optionally mirrored to Blob        | Immutable trail with relational lookups                                          |
| Sandbox working files                              | Sandbox filesystem, or a Drive if a later run needs them | Ephemeral by default, so copy anything you keep to Blob before the sandbox stops |

## Run durable multi-step agents with Workflows

### Use WorkflowAgent or workflow steps for long-running research

Vercel Workflows is generally available for TypeScript and JavaScript, with a Python SDK in beta. Workflow functions carry the `'use workflow'` directive, and every function marked `'use step'` records its output as it completes. Vercel Functions execute the code, Vercel Queues deliver the steps, and managed persistence stores state and event logs. Runs have no duration limit, which matters because a single Vercel Function invocation defaults to 300 seconds and tops out at 800 seconds on Pro and Enterprise, or 30 minutes with the extended max duration beta.

The durable agent primitive is `WorkflowAgent` from `@ai-sdk/workflow`. This example assembles the workspace loop from tools defined later in the guide:

```typescript
import { WorkflowAgent, type ModelCallStreamPart } from '@ai-sdk/workflow';
import { getWritable } from 'workflow';
import { stepCountIs } from 'ai';
import { internalTools } from '@/lib/internal-tools';
import { sandboxTools } from '@/lib/sandbox-tools';
import { storeArtifact } from '@/lib/artifacts';

export type RunContext = { userId: string; runId: string };

export async function workspaceRun(prompt: string, ctx: RunContext) {
  'use workflow';

  const agent = new WorkflowAgent({
    model: 'anthropic/claude-fable-5',
    instructions:
      'You are a research agent for an internal workspace. Use internal tools for company data, use the sandbox for any code you write, and cite every claim.',
    tools: {
      ...internalTools(ctx), // trusted, runs in backend code
      ...sandboxTools(ctx), // untrusted execution in Vercel Sandbox
    },
    stopWhen: stepCountIs(40), // budget for the whole run
  });

  const result = await agent.stream({
    messages: [{ role: 'user', content: prompt }],
    writable: getWritable<ModelCallStreamPart>(),
  });

  const pathname = await storeArtifact(ctx.runId, 'report.md', result.text);
  return { pathname };
}
```

The tools are built by factory functions that close over `ctx`, so every tool call knows which user started the run and which run it belongs to. The prerequisite [guide](https://vercel.com/kb/guide/how-to-run-a-multi-step-research-agent-on-vercel) covers the surrounding route handlers, `start()` and `getRun()` from `workflow/api`, and the `next.config.ts` wrapper.

### Add budgets, retries, approvals, and cancellation

Production runs need four controls, and Workflows plus the AI SDK provide each one without extra infrastructure.

- **Step budget**: `stopWhen: stepCountIs(n)` caps the loop. The prerequisite guide uses 25 steps for a search-heavy agent. Raise it for workspaces that call many small internal tools, and pair it with an AI Gateway [API key budget](https://vercel.com/docs/ai-gateway/observability-and-spend/api-key-budgets) so model spend has a hard ceiling.
  
- **Retries**: every `'use step'` function retries automatically, and completed steps never run twice. Put side effects inside steps so a retry repeats a whole operation rather than half of one.
  
- **Approvals**: set `needsApproval: true` on any tool that writes to an internal system or exports data. The run suspends until a person responds, without consuming compute while it waits.
  
- **Cancellation**: keep the `runId` in your run record so an operator can cancel from the UI, and check the run status before starting expensive sandbox work. Sandboxes you created for the run should stop when the run ends, whether it completed or was cancelled.
  

### Stream progress from the same durable run

`WorkflowAgent` writes model output and tool events to a persisted stream through `getWritable()`. The workspace UI reads that stream by `runId`, so a user who closes the tab and returns an hour later reconnects to the same run and sees the same progress. The stream is a view onto the run, not a separate channel you have to keep alive. Every step, input, output, and error is also visible in the Vercel dashboard under **Observability > Workflows**.

## Expose internal APIs as trusted tools

### Put internal API calls behind narrow typed tools

Internal API access is the requirement that pushes teams toward self-hosting, and it's the one this architecture handles most directly. An internal API tool is a `tool()` from the AI SDK whose `execute` function runs as a workflow step. The model fills in validated arguments. Your backend code makes the call with credentials the model never sees.

```typescript
import { tool } from 'ai';
import { z } from 'zod';
import type { RunContext } from '@/workflows/workspace';

export function internalTools(ctx: RunContext) {
  return {
    getCustomerSummary: tool({
      description: 'Read a customer summary from the internal platform API',
      inputSchema: z.object({
        customerId: z.string().regex(/^cus_[a-z0-9]+$/),
      }),
      execute: async ({ customerId }) => {
        'use step'; // checkpointed and retried by Workflows

        const res = await fetch(
          `${process.env.INTERNAL_API_ORIGIN}/customers/${customerId}/summary`,
          {
            headers: {
              authorization: `Bearer ${process.env.INTERNAL_API_TOKEN}`,
              'x-run-id': ctx.runId,
            },
          },
        );
        if (!res.ok) throw new Error(`Internal API returned ${res.status}`);

        // Return only the fields the model needs
        const { name, plan, openTickets } = await res.json();
        return { name, plan, openTickets };
      },
    }),
  };
}
```

Three properties make this tool safe to hand to a model:

- The input schema rejects anything that isn't a well-formed customer ID, so the model can't turn the tool into a generic HTTP client.
  
- The `execute` function calls one operation on one origin.
  
- The return value is trimmed to the fields the research task needs, keeping sensitive columns out of the model context and artifacts.
  

Write one tool per operation you're willing to expose. Exposing `queryInternalApi(path, method, body)` with arbitrary paths turns the tool into a proxy for the model and defeats the boundary.

### Apply user authorization and per-tool credentials

The run inherits the identity of the person who started it. Pass that identity into each tool call and authorize at the internal API, so the workspace can never read data its user couldn't read in the source system.

```typescript
// lib/internal-tools.ts (excerpt)
execute: async ({ customerId }) => {
  'use step';

  // Your own function: exchange the platform identity for a short-lived,
  // read-only token scoped to this user and this operation.
  const token = await mintInternalToken({
    subject: ctx.userId,
    scope: 'customers:read',
    ttlSeconds: 300,
  });

  const res = await fetch(
    `${process.env.INTERNAL_API_ORIGIN}/customers/${customerId}/summary`,
    { headers: { authorization: `Bearer ${token}`, 'x-run-id': ctx.runId } },
  );

  if (res.status === 403) {
    // Tell the model, don't throw. It can continue with other sources.
    return { error: 'This user does not have access to that customer' };
  }
  if (!res.ok) throw new Error(`Internal API returned ${res.status}`);
  return res.json();
},
```

`mintInternalToken` stands in for whatever your identity layer provides, such as a token exchange against your identity provider or a signed service token. On Vercel, deployments receive a `VERCEL_OIDC_TOKEN` that identifies the project and environment. If your internal API can trust an OIDC issuer, use that token directly and skip the long-lived `INTERNAL_API_TOKEN` entirely. The [Docker KB guide](https://vercel.com/kb/guide/docker) describes how Vercel provides the token, and the [AWS RDS with eve guide](https://vercel.com/kb/guide/give-eve-agent-secure-access-to-aws-rds-database) shows OIDC federation against a cloud provider.

Keep credentials per tool, not per workspace. Read-only tools get read-only scopes, and tools that open tickets get a write scope plus `needsApproval: true`.

### Use Static IPs or Secure Compute for private egress

By default, Vercel deployments can egress from any IP address, so an internal API that allowlists source IPs will reject them. You can solve this with:

- [Static IPs](https://vercel.com/docs/connectivity/static-ips) gives Pro and Enterprise projects a shared pool of static egress addresses. Add them to your internal API's firewall and keep application-level authentication in place, because an IP alone isn't an access control.
  
- [Secure Compute](https://vercel.com/docs/connectivity/secure-compute) is an Enterprise add-on that puts your Functions and build container on a dedicated private network with its own static IPs, a NAT gateway, and VPC peering into your AWS, Azure, or other private network. Use it when the internal API has no public endpoint.
  

Both apply to the trusted zone, which is where internal API tools run. Sandboxes can also reach a Secure Compute network when their network policy allows access to the private CIDR range, but this guide recommends keeping internal API access within workflow steps and leaving sandboxes without a route to production systems.

## Isolate code and browser tools

### Use Vercel Sandbox for model-written code and commands

Move work into a sandbox when the code is untrusted, when it parses untrusted input, or when it needs a shell. Each Vercel Sandbox is a Firecracker microVM with its own filesystem and network, booted from the `vercel/sandbox/universal` image by default, which includes Node.js, Python 3.14, and common utilities. When you need specific tooling, Custom OCI images from Vercel Container Registry replace the default.

```typescript
import { Sandbox } from '@vercel/sandbox';
import { tool } from 'ai';
import { z } from 'zod';
import type { RunContext } from '@/workflows/workspace';

export function sandboxTools(ctx: RunContext) {
  return {
    runAnalysis: tool({
      description:
        'Run a Python script over a dataset. The script reads dataset.json from its working directory and prints results to stdout.',
      inputSchema: z.object({
        script: z.string().describe('The Python script to execute'),
        dataset: z.string().describe('The dataset as a JSON string'),
      }),
      execute: async ({ script, dataset }) => {
        'use step';

        // One sandbox per run. A retried step reattaches to it.
        const sandbox = await Sandbox.getOrCreate({
          name: `research-${ctx.runId}`,
          timeout: 30 * 60 * 1000,
          networkPolicy: 'deny-all', // no egress, no DNS
        });

        await sandbox.writeFiles([
          { path: 'dataset.json', content: Buffer.from(dataset) },
          { path: 'analyze.py', content: Buffer.from(script) },
        ]);

        const cmd = await sandbox.runCommand({ cmd: 'python3', args: ['analyze.py'] });

        return {
          exitCode: cmd.exitCode,
          stdout: await cmd.stdout(),
          stderr: await cmd.stderr(),
        };
      },
    }),
  };
}
```

Two decisions in this code do the security work:

- `networkPolicy: 'deny-all'` blocks every outbound connection including DNS, so the script can compute over the dataset but can't send it anywhere.
  
- The dataset arrives as a tool argument that the trusted loop already fetched, so the sandbox never needs a credential or a route to the source.
  

### Separate sandbox network access from internal API access

The sandbox firewall makes the network boundary explicit. Each sandbox runs under one of three policies, and you can change the policy on a running sandbox without restarting it.

| Policy       | Behavior                                                                                                            | Use it for                                                         |
| ------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `allow-all`  | Unrestricted public internet. This is the default.                                                                  | Installing packages and pulling public data before locking down    |
| `deny-all`   | No outbound traffic, including DNS.                                                                                 | Running untrusted code over data you already placed in the sandbox |
| User-defined | Deny by default, then allow listed domains or CIDR ranges, with optional credential brokering and request proxying. | Tools that legitimately need one or two external services          |

The rule for a research workspace is that internal APIs are never in a sandbox allowlist. Trusted steps fetch internal data and write it into the sandbox. When model-written code needs an external service, allow that one domain and let the firewall inject the credential on the way out:

```typescript
import { Sandbox } from '@vercel/sandbox';

const sandbox = await Sandbox.create({
  networkPolicy: {
    allow: {
      // Broker the key at the firewall so it never enters the VM
      'ai-gateway.vercel.sh': [
        {
          transform: [
            { headers: { Authorization: `Bearer ${process.env.AI_GATEWAY_API_KEY}` } },
          ],
        },
      ],
      // Route every request to this data source through a proxy you control
      'api.partner-data.example.com': [
        { forwardURL: 'https://your-app.vercel.app/api/sandbox-proxy/partner' },
      ],
    },
  },
});
```

The `forwardURL` proxy is a Vercel Function. It receives the original request plus a `vercel-sandbox-oidc-token` header that identifies the team, project, and sandbox, so you can log every outbound request and reject paths you don't want the model to reach. The `defineSandboxProxy` helper validates the token for you:

```typescript
import { defineSandboxProxy } from '@vercel/sandbox/proxy';

const proxy = defineSandboxProxy(async (request, { sandboxId, projectId }) => {
  const path = request.headers.get('vercel-forwarded-path') ?? '';
  if (!path.startsWith('/v1/public/')) {
    return new Response('Path not allowed', { status: 403 });
  }
  console.log(JSON.stringify({ event: 'sandbox_egress', sandboxId, projectId, path }));
  return fetch(request);
});

// Sandboxes forward requests with their original method, so expose every verb
export {
  proxy as GET,
  proxy as POST,
  proxy as PUT,
  proxy as PATCH,
  proxy as DELETE,
};
```

Several firewall behaviors affect how you write policies. An empty user-defined policy behaves as `deny-all`. Leading wildcards such as `*.example.com` match subdomains but not `example.com` itself. Domain rules match on the TLS SNI, so plain HTTP and raw IP traffic must be allowed by CIDR range instead, and CIDR rules don't restrict DNS. The [Sandbox firewall docs](https://vercel.com/docs/sandbox/concepts/firewall) cover each case, including the domain-fronting caveat and how `transform` rules pin the `Host` header.

If a sandbox must reach a private network through Secure Compute, the sandbox's network policy decides whether that route exists. `allow-all` includes Secure Compute access, `deny-all` blocks it, and a user-defined policy must list the private CIDR range under `subnets.allow`. The REST API's create-sandbox endpoints also accept a `networkId` that pins a sandbox to a specific Secure Compute network.

Treat private sandbox egress as an exception you document, not the default, because it gives model-written code a route that only a proxy or credential brokering can constrain.

### Use one sandbox per run, task, or agent based on the risk boundary

| Granularity                                                            | When to use it                                                                        | Tradeoff                                                         |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| One sandbox per run                                                    | Most research workspaces. Analysis steps share files, and one VM amortizes boot time. | Everything in the run shares a filesystem                        |
| One sandbox per task                                                   | Tasks with different network needs, or tasks handling data from different tenants.    | More creations, which are billed but inexpensive                 |
| One sandbox per agent, or one Linux user per agent in a shared sandbox | Multi-agent runs where agents must not read each other's files.                       | User isolation is cheaper than separate VMs, but shares a kernel |

For the multi-agent case, `sandbox.createUser()` adds a Linux user with an isolated home directory, and a user's `runCommand` runs as that user:

```typescript
import { Sandbox } from '@vercel/sandbox';

const sandbox = await Sandbox.create();
const researcher = await sandbox.createUser('researcher');
const coder = await sandbox.createUser('coder');

await researcher.writeFiles([
  { path: 'notes.md', content: Buffer.from('private notes') },
]);

// The coder agent can't read the researcher's home directory
const denied = await coder.runCommand({
  cmd: 'cat',
  args: ['/home/researcher/notes.md'],
});

console.log(denied.exitCode); // non-zero: permission denied
```

User and group names must be at most 32 characters. The [multi-agent docs](https://vercel.com/docs/sandbox/concepts/multi-agent) cover shared groups for files agents should exchange.

### Plan browser automation with Marketplace browser services or a sandbox image

Browser tools need the same isolation as code tools plus two things code tools don't. Sessions are long-lived and stateful, and real sites push back with bot detection and CAPTCHAs.

You can solve this with two architecture choices:

- **Marketplace browser infrastructure.** [Browserbase](https://vercel.com/marketplace/browserbase) and [KERNEL](https://vercel.com/marketplace/kernel) provide sandboxed cloud browsers as Vercel Marketplace integrations with unified billing. Installing one adds an API key to your project, and your workflow steps drive the browser over the provider's SDK or the Chrome DevTools Protocol. Choose this path when the workspace browses the open web, needs live view or session replay, or needs anti-detection features. Treat the provider as a separate trust zone with its own credential, and pass it the same scoped inputs you'd pass a sandbox.
  
- **A browser inside Vercel Sandbox.** Run Chromium in the sandbox itself, driven by [agent-browser](https://agent-browser.dev/) from Vercel Labs or a similar CDP client. This is the pattern eve uses: its `@agent-browser/eve` extension runs Chromium and agent-browser inside the agent's sandbox, restricts navigation with an `allowedDomains` list, and never exposes cookie or saved-auth commands to the model. Install the browser at runtime, or build it into a custom image in Vercel Container Registry and pass it as `image` to `Sandbox.create()` so sessions start warm. Choose this path when the browser must stay inside a network policy you control, for example when it signs into an internal dashboard and should reach nothing else.
  

In both cases, the browser session is a tool the control plane calls. Keep the agent loop in Workflows, keep internal credentials out of the browser environment, and write screenshots and extracted data to Blob as evidence.

## Store artifacts and evidence outside the run

### Persist reports, evidence, and event logs

Anything a person or a downstream system needs after the run belongs in a private Vercel Blob store, written from a workflow step so a retry overwrites the same object:

```typescript
import { put } from '@vercel/blob';

export async function storeArtifact(
  runId: string,
  name: string,
  body: string | Buffer,
) {
  'use step';

  const blob = await put(`runs/${runId}/${name}`, body, {
    access: 'private',
    allowOverwrite: true, // a retried step writes the same object
  });

  return blob.pathname;
}
```

Use a consistent key layout such as `runs/{runId}/report.md`, `runs/{runId}/evidence/{n}.html`, and `runs/{runId}/events.jsonl`. Evidence files allow a reviewer to check a citation after the source page changes, so capture them at fetch time rather than reconstructing them later.

### Return downloadable artifacts to the workspace UI

Private stores have no public URLs. Serve downloads via a route that checks whether the user owns the run, then either streams the object using the Blob SDK or issues a signed URL with a short expiry. Signed URLs are scoped to a single operation and pathname, expire after 1 hour by default, and can be extended to up to 7 days. The [Private Storage](https://vercel.com/docs/vercel-blob/private-storage) and [Signed URLs](https://vercel.com/docs/vercel-blob/vercel-signed-urls) docs cover both approaches.

Store the returned `pathname` in the run record because Signed URLs expire, and the pathname is what you need to sign a new one.

### Keep audit records for tool calls and approvals

Workflows already records every step's input, output, and error in the dashboard. For compliance and debugging across runs, also write an application-level audit event for each tool call. The minimum useful record:

| Field                      | Purpose                                                                                             |
| -------------------------- | --------------------------------------------------------------------------------------------------- |
| `runId`                    | Joins the event to the workflow run and the artifacts                                               |
| `userId`                   | Who the run acted on behalf of                                                                      |
| `tool`                     | Which tool was called                                                                               |
| `inputHash`                | SHA-256 of the tool arguments, so you can prove what was requested without storing sensitive inputs |
| `approvalState`            | `not_required`, `pending`, `approved`, or `denied`, with the approver's ID                          |
| `sandboxId`                | Which sandbox executed the step, when one was used                                                  |
| `artifactPaths`            | Blob pathnames the step produced                                                                    |
| `startedAt`, `completedAt` | Timing for cost and latency analysis                                                                |

Write the event from inside the step, after the operation completes, so a retried step produces one final record.

## Compare Vercel with Kubernetes, Temporal, LangGraph, E2B, Modal, and Daytona

The comparison is at the architecture level. Vendor features change, so confirm current details against each provider's documentation before a procurement decision.

| Approach                            | Orchestration                                                        | Isolation for code and browser tools                                       | Internal API access                                                                           | What you operate                                              | Fits best when                                                                                                                  |
| ----------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Vercel (this guide)                 | Vercel Workflows, managed and generally available for TypeScript     | Vercel Sandbox microVMs with an egress firewall, plus Marketplace browsers | Trusted workflow steps, with Static IPs or Secure Compute for allowlists and private networks | Application code, tool schemas, network policies              | The workspace is a web application, the team writes TypeScript, and nobody wants to run orchestration or sandbox infrastructure |
| Kubernetes with Temporal            | Temporal server or Temporal Cloud plus a worker fleet you deploy     | gVisor, Kata, or Firecracker nodes you configure and patch                 | In-cluster networking                                                                         | Cluster, workers, sandbox tier, upgrades, observability stack | Temporal is already a company standard, or the workload includes always-on non-HTTP services                                    |
| LangGraph with E2B                  | LangGraph runtime, self-hosted or on its managed platform            | E2B managed sandboxes                                                      | From wherever the orchestrator runs                                                           | Orchestrator hosting and two vendor relationships             | The team is Python-first and wants explicit graph definitions                                                                   |
| Modal or Daytona as a sandbox plane | Bring your own orchestrator                                          | Provider-managed sandboxes                                                 | From your orchestrator host                                                                   | The orchestrator, plus integration with the sandbox provider  | An existing sandbox investment, or GPU and batch workloads the workspace hands off to                                           |
| AWS Bedrock AgentCore               | Agent hosting with session isolation, not a durable execution engine | AgentCore runtime sessions with built-in code interpreter and browser      | Inside your AWS VPC                                                                           | An AWS account and IAM                                        | The organization is standardized on AWS and wants agent hosting in-account                                                      |

### When Vercel should be the primary platform

Choose Vercel as the primary platform when the workspace is a web application with a UI, an API, and agents that run for minutes to hours, when tools are HTTP calls and sandboxed commands, and when you want durability, model routing, isolation, and artifact storage from one platform with one deployment model. The control plane, the trusted tools, and the sandboxes all deploy with `vercel deploy` or a Git push, and the dashboard shows workflow steps, sandbox sessions, and model calls together.

### When to keep a separate sandbox plane

Keep or add a separate sandbox plane when you need GPUs for the sandboxed work itself, when a browser fleet needs residential proxies or anti-detection features beyond what Marketplace providers offer, when sandboxes must live inside a specific VPC with custom routing that a network policy can't express, or when an existing sandbox contract already covers the workload. In each case the Vercel control plane still calls the external plane from a workflow step, and the boundary rules in this guide still apply.

### When self-hosted orchestration still fits

Self-hosted orchestration fits when the company has standardized on Temporal or Kubernetes operators and the research workspace is one of many workloads on that platform, when the agent code is in a language the Workflow SDK doesn't cover yet, or when the workspace depends on stateful workers that hold connections open indefinitely. Vercel can still host the UI and the trusted API layer in front of that orchestrator, and Secure Compute can connect the two privately.

## Operate the workspace in production

### Observe workflow runs, sandbox sessions, and model calls

The dashboard shows three views that together cover a run:

- **Observability > Workflows** lists every run with its steps, inputs, outputs, and errors to inspect.
  
- **Observability > Sandboxes** shows sessions, their resource use, and their network policy, and lets an operator stop a sandbox from the dashboard.
  
- AI Gateway logs every model call with token counts, latency, and cost. Tag each of them with the `runId` so an operator can move from a slow report to the step, the sandbox, and the model call that caused it.
  

### Control secrets and environment access

Secrets stay in the trusted zone. Internal API tokens, Blob tokens, and provider keys are project environment variables that workflow steps read. Sandboxes receive only the environment variables a command needs, passed in the `env` option of `runCommand`, and anything a sandbox must send to an external service goes through firewall credential brokering so the secret never enters the VM. Prefer the deployment's `VERCEL_OIDC_TOKEN` over long-lived static tokens wherever the receiving system can validate it.

### Set cleanup, retention, and cost guardrails

- **Sandbox lifetime**: set the shortest `timeout` that fits the task and call `sandbox.stop()` when the run finishes. On Pro and Enterprise, a session can run up to 24 hours, and a persistent sandbox can stop and resume beyond that, so an unbounded sandbox is a cost you chose rather than a limit you hit.
  
- **Snapshots and drives**: snapshots expire 30 days after last use by default. Set an explicit retention period for anything you keep longer.
  
- **Step and spend caps**: keep `stopWhen` on every agent and attach a budgeted AI Gateway key so a runaway loop stops at a known cost.
  
- **Artifact retention**: decide how long evidence files live and enforce it with a scheduled cleanup job. Reports are cheap to keep. Raw fetched pages add up.
  
- **Spend Management**: configure alerts or an automatic pause at a team spend threshold that covers Sandbox, Functions, and Workflows usage.
  

### Plan availability and limits

| Resource                             | Hobby         | Pro                                     | Enterprise                              |
| ------------------------------------ | ------------- | --------------------------------------- | --------------------------------------- |
| Vercel Sandbox max session duration  | 45 minutes    | 24 hours                                | 24 hours                                |
| Vercel Sandbox concurrent sandboxes  | 10            | 10,000                                  | 10,000                                  |
| Vercel Sandbox max vCPUs per sandbox | 4             | 8                                       | 32                                      |
| Vercel Workflows                     | Available     | Available                               | Available                               |
| Static IPs                           | Not available | Available                               | Available                               |
| Secure Compute                       | Not available | Not available                           | Add-on                                  |
| Vercel Functions max duration        | 300 seconds   | 800 seconds, 30 minutes extended (beta) | 800 seconds, 30 minutes extended (beta) |

Functions default to 300 seconds on every plan. Workflow steps can opt into the 30-minute extended duration with the `VERCEL_ENABLE_WORKFLOW_EXTENDED_MAX_DURATION` environment variable on Pro and Enterprise. Sandbox creations are billed per creation, and downloads into a sandbox are free, so a per-task sandbox strategy costs little more than a per-run one. See [Vercel Functions limits](https://vercel.com/docs/functions/limitations), [Sandbox pricing and quotas](https://vercel.com/docs/sandbox/pricing), and [Workflow pricing and limits](https://vercel.com/docs/workflows/pricing) for current figures.

## Troubleshooting

### Sandbox commands fail with network errors

User-defined policies with no allowed domains behave as `deny-all`, which blocks DNS as well as connections. Check that the domain you need is listed, that you added the apex domain separately if you used a leading wildcard, and that plain HTTP or IP-literal traffic is allowed by CIDR range rather than by domain.

### Sandbox or AI Gateway calls fail locally with an authentication error

Both authenticate with the project's OIDC token on Vercel. Locally, run `vercel link` and `vercel env pull` to fetch a development token, and re-run `vercel env pull` when it expires.

### The internal API rejects requests from Vercel

Vercel deployments egress from any IP address by default. Add Static IPs to the project and allowlist them, or use Secure Compute for private connectivity. Then confirm the tool is sending the credential the API expects.

### A long run stopped after a deployment

Workflows resumes runs across deployments by replaying completed steps. If a run appears stuck, check the run in **Observability > Workflows** for a step waiting on approval or a failing retry. Side effects outside a `'use step'` boundary are the usual cause of duplicated work after a resume.

### Files written in the sandbox are gone

The sandbox filesystem is ephemeral once the sandbox stops. Write anything you need to Blob from the same step that produced it, or mount a Drive when a later run needs the files.

### Browser sessions time out

Check the sandbox `timeout` and extend it with `extendTimeout()` for long sessions, or check the Marketplace provider's session limits. Split browser tasks that outlast a single tool call into steps so each one checkpoints.

## Next steps

- Build the prerequisite loop in [How to run a multi-step research agent on Vercel](https://vercel.com/kb/guide/how-to-run-a-multi-step-research-agent-on-vercel)
  
- Compare stack options for approval-gated agents in [Durable agent approval workflows on Vercel](https://vercel.com/kb/guide/agent-approval-workflow-stack-guide)
  
- Read the [Sandbox firewall docs](https://vercel.com/docs/sandbox/concepts/firewall) for network policies, credential brokering, and request proxying
  
- Isolate agents in one VM with [Run isolated AI agents in one sandbox](https://vercel.com/docs/sandbox/concepts/multi-agent)
  
- Learn how steps, sleeps, and hooks work in the [Workflow concepts docs](https://vercel.com/docs/workflows/concepts)
  
- Review the agent primitives in the [AI SDK 7 changelog](https://vercel.com/changelog/ai-sdk-7)
  
- Connect to private backends with [Secure Compute](https://vercel.com/docs/connectivity/secure-compute) and [Static IPs](https://vercel.com/docs/connectivity/static-ips)
  
- Ship custom tool runtimes with [Running Docker on Vercel](https://vercel.com/kb/guide/docker)
  
- See a Python variant with isolated repository inspection in [Build an agentic app in FastAPI with OpenAI Agents API and Vercel Sandbox](https://vercel.com/kb/guide/fastapi-openai-agents-api-vercel-sandbox)