Overview

Platform Template

The wall-to-wall reference for building an AI app builder on Vercel. Sandboxes, AI Gateway, deployments, Sign in With Vercel, and project transfers

The Platform Template is the wall-to-wall reference for building an AI app builder on Vercel. It covers every layer of the stack: running AI agents in sandboxes, routing LLM calls through AI Gateway, previewing apps live, deploying to Vercel, and transferring project ownership to users via Vercel Apps and claim deployments.

Check out the live demo at platform-template.labs.vercel.dev and the GitHub source

What This Covers

This template shows every Vercel platform primitive working together end-to-end:

  • Vercel Sandbox - agents write code and run dev servers allowing for app previews
  • AI Gateway - secure LLM access via OIDC, proxied from sandboxes that can't hold credentials
  • Vercel Deployments - deploy sandbox contents to production with the Vercel SDK
  • Vercel Apps - OAuth flow that installs an app in the users Vercel account during the claim flow allowing for continued updating
  • Project Transfers - allow users to get their websites deployed and then later take ownership of them

Architecture Overview

The template is a Next.js application with five major subsystems:

How It Works

1. User sends a prompt

The user types a prompt in the chat interface. The frontend calls rpc.chat.send - a streaming oRPC procedure that orchestrates the entire flow.

2. Sandbox is created and set up

If this is a new session, a Vercel Sandbox is provisioned. The setup process installs bun, scaffolds the chosen template (Next.js, Vite, or TanStack Start), installs the agent CLI, and starts the dev server:

lib/sandbox/setup.ts
export async function* setupSandbox(
  sandbox: Sandbox,
  options: SetupOptions,
): AsyncGenerator<SetupProgress> {
  yield { stage: "installing-bun", message: "Installing bun..." };
  await run(sandbox, {
    cmd: "sh",
    args: ["-c", "curl -fsSL https://bun.sh/install | bash && ..."],
    sudo: true,
  }, "bun install");

  // Run template-specific setup (create-next-app, create-vite, etc.)
  for await (const progress of template.setup(sandbox)) {
    yield { stage: progress.stage, message: progress.message };
  }

  // Install agent CLI and wait for dev server in parallel
  yield { stage: "installing-agent", message: "Installing agent..." };
  await Promise.all([agentInstallPromise, devServerPromise]);

  yield { stage: "ready", message: "Sandbox ready" };
}

3. Agent executes inside the sandbox

Native agent coding CLIs like Codex and Claude Codex are run inside of the sandbox

lib/agents/claude-agent.ts
const env = {
  ANTHROPIC_BASE_URL: proxyConfig.baseUrl,  // Points to our proxy
  ANTHROPIC_API_KEY: proxyConfig.sessionId,  // Short-lived session ID
};

const cmd = await sandbox.runCommand({
  cmd: "sh",
  args: ["-c", `claude --print --verbose --output-format stream-json ...`],
  cwd: SANDBOX_BASE_PATH,
  env,
  detached: true,
});

for await (const log of cmd.logs()) {
  // Parse NDJSON and convert to unified StreamChunk format
}

4. LLM calls go through AI Gateway

The agent CLI's base URL is configured to the platform's proxy route, which acts as a provider endpoint. The proxy validates the session, attaches a Vercel OIDC token, and forwards the request to AI Gateway.

If you wanted to implement things like tracking token spend per user this is where you're able to implement it:

app/api/ai/proxy/[[...path]]/route.ts
// 1. Extract session ID from x-api-key or Authorization header
const data = await redis.get(`session:${sessionId}`);
if (!session) return new Response("Invalid session", { status: 401 });

// 2. Get OIDC token from the Vercel deployment
const gatewayToken = await getVercelOidcToken();

// 3. Forward to AI Gateway with only allowed headers
headers.set("authorization", `Bearer ${gatewayToken}`);
const response = await fetch(`${AI_GATEWAY_URL}${apiPath}`, {
  method: request.method,
  headers,
  body: await request.arrayBuffer(),
});

5. User previews and deploys

The sandbox dev server is exposed via an iframe. When the user clicks Deploy, the platform reads all files from the sandbox and creates a Vercel deployment:

lib/rpc/procedures/deploy.ts
const { client, teamId, ownership } = await getDeploymentClient(projectId);
const files = await readFilesForDeploy(sandbox, filePaths);

await vercel.deployments.createDeployment({
  name,
  files,
  target: "production",
  project: projectId ?? undefined,
});

6. User claims the deployment

For signed-out users, the deployment lives on the partner team. The user can sign in with Vercel to claim it:

  1. Platform creates a transfer request via the Vercel API, getting a transferCode
  2. OAuth URL includes transfer_code - Vercel transfers the project during authorization
  3. Post-OAuth callback stores per-project tokens (JWE-encrypted) in Redis
  4. Future deploys use the stored tokens to deploy directly to the user's account

AI Gateway Proxy Pattern

Sandboxes cannot hold secrets. The proxy pattern solves this with short-lived capability tokens:

Security properties:

  • Sandbox never sees the real OIDC token or any API key
  • Proxy sessions are stored in Redis with a 1-hour TTL
  • Only accept, content-type, and anthropic-version headers are forwarded
  • CORS checks reject cross-origin requests

Deployment Authorization Model

The template implements three tiers of deployment authorization:

TierWhenToken SourceDeploys To
PartnerSigned-out userVERCEL_PARTNER_TOKEN env varPartner team
UserSigned-in userOAuth session cookieUser's account
ProjectPost-claimStored per-project tokens (Redis)User's account

Priority order: project tokens > user session > partner token. This means once a user claims a project, all subsequent deploys go to their account even if they're on a different browser session, because the platform stored the tokens during the claim flow.

Sign in With Vercel + Claim Flow

The claim flow combines OAuth authorization with project transfer in a single redirect:

  1. User clicks "Claim" on a partner-owned deployment
  2. Platform calls projects.createProjectTransferRequest() to get a transferCode
  3. User is redirected to Vercel OAuth with transfer_code in the URL
  4. User authorizes - Vercel transfers the project and returns an auth code
  5. Callback exchanges the code for tokens, stores them (JWE-encrypted) in Redis
  6. User is redirected back with ?sandboxId=xxx to restore their session

State recovery is seamless: the sandbox ID is encoded in the redirect URL, and usePersistedChat() restores messages, preview URL, and deployment state from Redis.

Getting Started

Step 1: Clone and Set Up

Terminal
git clone https://github.com/vercel/platform-template
cd platform-template
pnpm install

The template is built with:

  • Next.js 16 with App Router
  • oRPC for type-safe streaming RPC
  • Vercel Sandbox for secure code execution
  • Vercel SDK for deployments and project management
  • Zustand + SWR for state management
  • Upstash Redis for session persistence

Step 2: Configure Environment Variables

.env.local
# Required for AI Gateway proxy
PROXY_BASE_URL="http://localhost:3000/api/ai/proxy"

# Required for deployments (partner-tier)
VERCEL_PARTNER_TOKEN=""
VERCEL_PARTNER_TEAM_ID=""

# Required for Sign in With Vercel
VERCEL_CLIENT_ID=""
VERCEL_CLIENT_SECRET=""
SESSION_SECRET=""

# Required for session persistence
UPSTASH_REDIS_REST_URL=""
UPSTASH_REDIS_REST_TOKEN=""

Step 3: Run the Development Server

Terminal
pnpm dev

Open http://localhost:3000 to see the platform. You'll see:

  • Chat interface - describe what you want to build
  • Agent selector - choose between Claude Code and Codex
  • Template selector - pick Next.js, Vite, or TanStack Start
  • Live preview - watch your app being built in real-time
  • File explorer - browse generated files
  • Deploy button - deploy to Vercel with one click

Step 4: Build Something

Try a prompt like:

Prompt
"Build a dashboard with a sidebar navigation, a main content area showing some charts, and a dark mode toggle. Use shadcn/ui components."

The platform will create a sandbox, set up the template, run the agent, and show you a live preview as the code is being written.

Key Patterns

Unified Stream Protocol

Both Claude Code and Codex produce vastly different output formats. The harness layer normalizes them into a single StreamChunk protocol that the UI consumes. This means adding a new agent (e.g., Gemini CLI, OpenCode) requires no UI changes - just implement the AgentProvider interface and parse the CLI output.

oRPC Generator Streaming

The RPC layer uses async function* generators for streaming. The client gets typed chunks as they're yielded:

lib/rpc/procedures/chat.ts
export const sendMessage = os.input(schema).handler(async function* ({ input }) {
  yield { type: "sandbox-id", sandboxId: sandbox.sandboxId };

  for await (const progress of setupSandbox(sandbox, options)) {
    yield events.sandboxStatus(sandbox.sandboxId, ...);
  }

  for await (const chunk of agent.execute({ prompt, sandboxContext, proxyConfig })) {
    yield chunk;
  }
});

Template System

Each template implements a setup generator and provides framework-specific agent instructions:

lib/templates/types.ts
interface Template {
  id: TemplateId;
  name: string;
  devPort: number;
  instructions: string;   // System prompt for the agent
  setup(sandbox: Sandbox): AsyncGenerator<SetupProgress>;
}

Templates handle all scaffolding: create-next-app, create-vite, installing shadcn, configuring Tailwind, and starting the dev server. The agent receives framework-specific instructions so it generates idiomatic code.


The Platform Template demonstrates what's possible when you combine Vercel's infrastructure primitives - sandboxes, AI Gateway, deployments, and project transfers - into a cohesive product experience. It's the reference architecture for building platforms where AI writes code, users preview it live, and deploy it to production with a single click.