Outposts is in early access. APIs and CLI commands described here may change.
Contact your account team to enable Outposts for your organization.
- Sessions to run inside your network, next to internal services, registries, and secrets
- Custom hardware profiles (e.g. GPUs, large memory machines, specific OS images)
- Existing dev box, VM, or Kubernetes infrastructure to host Devin workloads
- Enterprise controls over network access, build outputs, and monitoring
How it works
Outposts has two layers:-
The worker (e.g.
devin worker start) — a binary that you run on a machine to serve a single queued session. It opens an outbound connection to Devin’s cloud and executes the session’s tool calls locally. Cognition provides this binary; you never need to implement it. The Devin CLI contains the logic for fetching and executing this binary. - The orchestrator — software that watches the fleet API for sessions waiting on workers, provisions a VM or container for each one, and starts the worker inside it. We provide some reference-implementations for common platforms (like Kubernetes), but you should feel free to adapt these (they’re open source!) or write your own.
Prerequisites
- An organization with Outposts enabled
- A v3 API token with the appropriate Outposts scopes:
account.outposts.orchestratorfor orchestrators managing pools (implies the machine scope)account.outposts.machinefor workers reading the queue and claiming/releasing sessions
- A machine image (VM or container) with:
- The Devin CLI installed
- The machine dependencies below
- Your repositories cloned, with configured remotes
- Access to the build tools, package registries, secrets, and internal services your sessions need
Machine dependencies
Sessions execute directly on your machine, so the worker relies on tools you install there. Required| Dependency | Used for |
|---|---|
git (on PATH) | Cloning and all repository operations |
| Dependency | Feature |
|---|---|
ffmpeg (on PATH) | Devin’s screen-recording features. Without it, sessions cannot record the screen. |
| Chrome or Chromium | Browser and computer-use features. The worker looks for Chrome in standard install locations by default; set DEVIN_CHROME_PATH in the worker’s environment to the absolute path of the binary to override (e.g. DEVIN_CHROME_PATH=/usr/bin/google-chrome). Without it, browser tools are unavailable. |
Passwordless sudo | Lets Devin install software it needs during a session (e.g. missing build tools or system packages). Only grant this when the machine is dedicated to Devin and recycled after each session — never on shared or long-lived machines. |
The core flow
1. Register a pool
A pool is a named queue of sessions served by your infrastructure (for example,rhel, gpu-h200, or my-pool). Create one with devin worker pool create:
In the fleet API, pools are represented as
pools resources, scoped to
your account (shared across all of its organizations).2. Poll the fleet API for waiting sessions
Your orchestrator lists pending sessions for the pools it serves:items:
first to the page size (up to 200), then pass each response’s
cursor into the next request while has_next_page is true:
metadata.session_id rather than
treating every item as new. When has_next_page becomes false, save the
returned cursor as the starting position for the watch API.
Watch for changes
After the initial list, start a Server-Sent Events (SSE) watch with the final cursor:MODIFIED events when a session’s queue entry changes and
DELETED events when it is removed. Newly queued sessions also arrive as
MODIFIED events. Each SSE data field contains JSON with this shape:
cursor after processing it. If the connection
closes, reconnect with the last persisted cursor to replay any changes that
occurred while disconnected. Watch delivery is also at least once, so clients
must tolerate duplicate events. Streams end after at most five minutes; a
reconnecting watch loop is expected.
The pool filter applies to both list and watch requests. The phase and
acceptor_id filters apply only to list requests and are ignored when
watch=true; filter watched events using the fields in each event’s object.
Omitting the cursor starts from the beginning, so use list-then-watch for
normal reconciliation.
Before starting a machine for a session, atomically claim it so no other worker picks it up. Pass an acceptor_id — a self-reported identity for your worker:
409. Claiming promises that a worker will be ready within the server-assigned claim deadline (status.claim_deadline); expired claims return to the queue automatically. If provisioning fails, release the claim so the session returns to the queue immediately:
3. Spawn a machine and run the worker
For each claimed session, provision a VM or container from your image. Inside it, run the worker from the directory where the session’s repositories are already checked out:devin worker start is invoked:
repos
app
.git
infra
.git
devin worker start from repos/, and the session sees app/ and infra/ relative to its working directory.
Useful flags:
| Flag | Description |
|---|---|
--session | Identifies the session to serve; the worker exits when it ends. |
--pool | Identifies the pool for the session to serve. |
--acceptor-id | Uses the same acceptor ID as the API claim for this worker. |
--token | Optional auth token for the worker. If omitted, the worker uses DEVIN_OUTPOSTS_TOKEN; if neither is set, the command errors. |
4. Fetching the remote binary directly
Thedevin worker start command automatically downloads the correct devin-remote binary. If you are building a custom orchestrator that does not use the Devin CLI, you can fetch the binary directly from:
| Suffix | OS / Architecture |
|---|---|
linux_x64 | Linux x86_64 |
macos_arm64 | macOS Apple Silicon |
windows_x64.exe | Windows x86_64 |
spec.remote_binary_sha, use that SHA instead of latest — it pins the session to a specific tested version.
Spawn contract
If your orchestrator launchesdevin-remote itself, spawn it as:
| Variable | Required | Description |
|---|---|---|
DEVIN_OUTPOST_GATEWAY_URL | Yes | Outpost gateway base URL, e.g. wss://outpost-gateway.devin.ai. |
DEVIN_OUTPOST_CONNECT_TOKEN | Yes | Bearer connect token for the gateway, from the claim response. |
DEVIN_OUTPOST_SESSION_ID | Yes | The session ID being served. All three DEVIN_OUTPOST_* variables must be set together. |
DEVIN_REMOTE_STATE_DIR | Strongly recommended | Per-session state directory where the remote stores its credentials, tokens, and shell-integration files. Use a unique directory per session (e.g. ~/.devin/worker/sessions/<session_id>, which is what devin worker uses). If unset, the remote falls back to a shared system-wide default (/opt/.devin on Linux, ~/.devin on macOS, C:\ProgramData\devin on Windows), which must then exist and be writable — and which leaks per-session state across concurrent sessions. Always set this. |
DEVIN_CHROME_PATH | Optional | Path to a Chrome/Chromium binary on the box for the browser tool (there is no Devin-managed Chrome on Outposts). |
DEVIN_OUTPOST_DESKTOP | Optional | Set to true to enable the desktop (VNC) stream. It is lazy on the remote side — nothing is captured until a viewer connects — so it is safe to enable unconditionally. |
PATH, HOME, USER, LOGNAME, TMPDIR, LANG, TZ, and — for the desktop stream’s screen capture on Linux/X11 — DISPLAY, WAYLAND_DISPLAY, XAUTHORITY). Do not leak anything the agent should not be able to see into the remote: it is inherited by the agent’s shell.
Additional lifecycle expectations:
- Working directory: launch the remote from the directory containing the session’s repositories (the same rule as
devin worker start). - Session end: when the session ends (sleeps or terminates), Devin notifies the remote and it exits with status 0 on its own. Treat a clean exit as the end of the session: confirm the queue entry’s
status.session_statusissuspendedorterminated(the status update can lag the exit by a few seconds, so re-read a few times), then release the claim. As a fallback, also pollstatus.session_statuswhile the remote runs and kill the process yourself once it reachesterminated(or the queue entry disappears).
5. Terminate the machine when the worker exits
Whendevin worker start exits, the session is over (or has been suspended). Terminate the VM or container. If your pool is resumable, snapshot the machine before terminating so you can restore it if the session resumes.
Your orchestrator can track its claimed sessions and their states:
status.session_status of pending, running, suspended, or terminated.
Centralization-free scheduling
Planning to run more than ~16 coordinators (workers or orchestrators
watching and claiming from a pool)? Contact your account team first — larger
fleets amplify claim contention and queue read load, and we want to make
sure the pool is provisioned for it.
- Claims are the only coordination primitive. Every worker independently watches the queue and races to claim pending sessions. The claim is an atomic compare-and-swap on the server: exactly one worker wins, and every loser gets a
409and simply moves on to the next pending session. Losing a claim race is normal operation, not an error. - Each worker has its own identity. The
acceptor_idscopes a worker’s claims, renewals, and restart recovery to that worker alone.devin worker startgenerates and persists one automatically per machine, so a fleet needs no identity configuration. Never share an acceptor ID (or a copied worker data directory) across machines — colliding workers will steal each other’s claims. - Failures self-heal. If a worker dies after claiming, its claim expires at the claim deadline and the session returns to the queue for another worker to pick up. No fleet-level health tracking is required.
- Use the watch endpoint, not repeated full lists. Do one paginated list to build initial state, then hold a watch stream from the returned cursor. Re-polling the entire queue from every worker scales poorly and adds claim latency; the watch stream delivers changes as they happen.
- Talk to us before going past ~16 machines on a pool. Coordination-free claiming works well at small fleet sizes, but larger fleets amplify claim contention and queue read load. If you plan to run more than about 16 workers against a single pool, contact your account team first so we can make sure the pool is provisioned for it.
API reference
All Outposts endpoints live underhttps://api.devin.ai/opbeta and share a common resource shape (metadata / spec / status). List responses return items, cursor, has_next_page, and total.
Devins (/outposts/devins)
| Endpoint | Description |
|---|---|
GET /outposts/devins?pool=...&phase=pending | List sessions waiting for a worker. |
GET /outposts/devins?pool=...&first=...&cursor=... | Continue a paginated list from a previous response cursor. |
GET /outposts/devins?pool=...&watch=true&cursor=... | Stream MODIFIED and DELETED events after a list or watch cursor. |
GET /outposts/devins?phase=claimed&acceptor_id=... | List sessions claimed by a given acceptor. |
GET /outposts/devins/{session_id} | Get a single queue entry. |
POST /outposts/devins/{session_id}/claim | Atomically claim a session (409 if already claimed). Body: {"acceptor_id": "..."}. |
POST /outposts/devins/{session_id}/release | Release a claim, returning the session to the queue. Body: {"acceptor_id": "..."}. |
Pools (/outposts/pools)
| Endpoint | Description |
|---|---|
GET /outposts/pools | List your account’s pools. |
POST /outposts/pools | Create a pool. Body: {"name": "my-pool", "platform": "linux", "description": "..."}. |
GET /outposts/pools/{pool_id} | Get a single pool. |
DELETE /outposts/pools/{pool_id} | Delete a pool (409 while it has active claims). |
status.queue_depth and status.active_claims are useful autoscaling signals: if the queue is backing up, your orchestrator can provision more warm machines.

