OpenAI Agents on Vercel: own the Queue and Sandbox hosting seam
On September 10, 2026, Vercel published a changelog for building with the OpenAI Agents API on Vercel. The split is blunt: OpenAI manages the agent loop and session state; Vercel hosts the application; each session connects to Vercel Sandbox for code and file work. Signed OpenAI webhooks plus Vercel Queues create or reconnect that Sandbox. You get an isolated environment per session, a persistent workspace across follow-ups, and scale-to-zero without an always-on worker.
This post is the Vercel hosting seam. It is not the harness ownership post, which covers what you still own when OpenAI hosts the Codex-style Agents API harness itself. Here the ICP question is narrower: what must live in your product repo this week so webhook verification, Queue consumption, Sandbox lifecycle, secrets placement, and mid-session Sandbox death are product behavior — not a changelog paraphrase.

Two paths, one product repo
Vercel’s step-by-step guide draws two independent paths:
- The agent path carries user input and streamed output between your app and OpenAI.
- The infrastructure path turns an OpenAI lifecycle webhook into a running Sandbox.
During a run the pieces connect in this order (verified against the public guide):
- The app creates a session with a
self_hostedenvironment and sends input. - OpenAI sends a signed webhook when the session needs an executor.
- The webhook places the session ID on a Queue and responds immediately.
- A private consumer reads the current session state and creates or resumes its Sandbox.
codex exec-serverconnects outbound from the Sandbox to OpenAI.- OpenAI runs the agent and streams session events back to the app.
The OpenAI environment ID identifies the executor connection for the life of a session. It is different from the Vercel Sandbox ID. The sample names the Sandbox deterministically from the OpenAI session ID (agents-${sessionId}). That naming rule belongs in your repo: retries must resolve to one microVM, not a flock of orphans.
What builders must put in the product repo this week
1. Webhook verification against the raw body
OpenAI delivers signed HTTP POSTs. Your public webhook route must verify the signature against the unmodified request body before JSON parsing. Invalid signatures return 400. Until OPENAI_WEBHOOK_SECRET is installed, the sample refuses deliveries with 503 — fail-closed on purpose.
Subscribe at least to:
agent.session.action_required— wake infrastructure when OpenAI needs anenvironment_connectionagent.session.failed— remove compute for a failed session
For environment wake-ups, queue only when required_action.type is environment_connection — a function_call is a different path. Use the OpenAI event ID as the Queue idempotency key so retries do not double-provision.
2. A Queue consumer that reconciles, then creates or reconnects
Provisioning can take longer than a webhook should stay open. Either side can retry. The Queue absorbs bursts and invokes a private consumer. On Vercel, handleCallback() alone does not subscribe the Function: you need the queue/v2beta experimental trigger in vercel.json mapped to your topic (the sample uses sandbox-wakeup). Without that trigger, messages enter the Queue and nobody consumes them — a silent stall that looks like “environment pending forever.”
The consumer must read the latest OpenAI session before acting. Queue delivery is at-least-once. Ignore deleted sessions and resolved actions. Start or reconnect using session.environment.id and session.environment.remote_url from retrieve — the connection webhook does not include connect.remote_url. OpenAI waits up to five minutes for the executor; configure client and proxy timeouts. If the wait expires, the submission can fail and leave the session failed.
3. Sandbox create/reconnect with persistence and locks
Use Sandbox.getOrCreate with a deterministic name, a managed image (the sample uses vercel/sandbox/node:24), persistent: true, and a timeout above your longest expected turn. Persistence preserves /workspace across stops; it does not keep codex exec-server running after a resume — restart the executor on reconnect.
Allow api.openai.com, codex-cloud-environments.chatgpt.com, and (unless the CLI is baked into a custom image) registry.npmjs.org. OS-level flock around setup and executor start prevents concurrent consumers from double-installing the CLI or starting competing processes. The Sandbox does not stream assistant output to the browser — it returns command results to OpenAI; your app streams OpenAI session events.
4. Where secrets live
The guide’s credential split is a product requirement:
| Secret | Where it lives | Purpose |
|---|---|---|
| Application key | Vercel Functions env | Create agents and manage sessions |
| Environment / executor key | Passed into the Sandbox only (CODEX_API_KEY) | Lets codex exec-server connect |
| Webhook signing secret | Functions env | Verify OpenAI deliveries |
| Sandbox / Queues auth | OIDC on Vercel (automatic in deployed Functions) | Do not stash a long-lived Vercel access token “just in case” |
Keep the application key and webhook secret out of the Sandbox. Keep the executor key out of the browser. If Deployment Protection is on, register the webhook with the automation bypass query param so OpenAI can reach that one URL without disabling protection for the whole deployment.
5. Failure modes when the Sandbox dies mid-session
Plan for these in the product repo:
- Webhook never arrives locally — OpenAI posts to the registered production URL. Local
devcan exercise session create and streaming; full lifecycle needs the deployed webhook. - 503 on webhook — missing signing secret. Fix: register → env add → redeploy.
- Stuck on environment-pending — webhook blocked (Deployment Protection), or Queue topic with no
queue/v2betaconsumer. - Sandbox up, executor never connects — network allowlist or CLI install failure. Probe before blaming OpenAI.
- Sandbox stopped / timed out mid-turn — persistent filesystem may remain; executor process does not. Reconnect on the next
environment_connection. - Session failed — consumer should delete the Sandbox. OpenAI does not send a deletion webhook when a session is deleted elsewhere, so periodically compare retained Sandboxes with session records and remove orphans.
- At-least-once duplicates — idempotency key + session retrieve + deterministic Sandbox name + OS locks. Implement all four.
Log webhook event ID, session ID, environment ID, Sandbox name, provisioning duration, and cleanup reason. Never log API keys or signatures.
11 production screens. Login, database, payments — all wired.
The SaaS Dashboard Kit ships everything already connected. Nothing to set up. Live demo at saas.otf-kit.dev.
Ownership of files vs OpenAI session state
Hold these domains apart:
- OpenAI session state — turns, compaction, agent loop, required actions, streamed events.
- Sandbox filesystem —
/workspacefiles in the microVM. Withpersistent: true, files survive normal stop/resume for that named Sandbox; they are not “in the session JSON.” Artifacts that must outlive the Sandbox belong in your object store or git-backed storage.
A follow-up that reads a file created earlier tests infrastructure reattach of the same workspace — not OpenAI storing file bytes in session state. That is also why the guide prefers Queues over Vercel Workflow for the wake-up: OpenAI already owns the long-running session; a second workflow clock does not remove reconciliation.
How this sits beside the harness post
| Concern | Where it lives | OTF post |
|---|---|---|
| Managed agent loop, compaction, subagents | OpenAI Agents API | Harness ownership |
| Webhook → Queue → Sandbox create/reconnect | Your Vercel app + Queues + Sandbox | This post |
| Tool permission boundaries for production writes | Your MCP / tool policy | Safe tool permissions |
| Repo conventions agents can see | Your product repo | Production repo conventions |
If you are choosing whether to call the Agents API at all, start with the harness post. If you already chose Agents API on Vercel with self_hosted + Sandbox, stay here until the infrastructure path is boring.
Patterns that keep product code in a repo you control — including https://otf-kit.dev and https://github.com/otf-kit/sdk — still matter: the agent can write Sandbox files, but ship checklist, schema, and auth policy stay in the repository your team reviews.
A practical adoption sequence
- Create separate application and environment keys in the same OpenAI org/project.
- Create the reusable agent; store
OPENAI_AGENT_IDas config. - Session create with
environment: { type: "self_hosted", workspace_directory: "/workspace" }. - Ship webhook with raw-body signature verification and Queue publish (idempotent on event ID).
- Ship private Queue consumer: session retrieve →
getOrCreate→codex exec-server, plusvercel.jsonqueue/v2betatrigger. - Deploy, register production webhook, install
OPENAI_WEBHOOK_SECRET, redeploy. - Prove the loop: write a
/workspacefile, follow up to read it, delete session, confirm Sandbox cleanup. - Add orphan Sandbox reconciliation and structured logs before customers hit the feature.
What not to do this week
- Do not run Sandbox provisioning inline in the webhook response and hope OpenAI’s timeout is generous.
- Do not put the application key inside the Sandbox “to simplify.”
- Do not skip the
queue/v2betatrigger becausehandleCallback“looks subscribed.” - Do not treat OpenAI session deletion as automatic Sandbox deletion.
- Do not collapse this hosting seam into the harness ownership checklist — different failure modes, different owners.

Sources
- Build with OpenAI Agents API on Vercel (Vercel changelog, September 10, 2026)
- Build an agent with OpenAI Agents API on Vercel (Vercel KB guide)
- Vercel Sandbox docs
- Session webhooks (OpenAI Agents API)
- Related internal: OpenAI Agents API harness ownership
Ship the product, not the setup.
- 11 production screens — auth, billing, team, analytics, settings
- Real database, payments, and login — all wired on day 1
- AI configs pre-tuned so your agent extends instead of regenerates