Skip to main content
Complete reference for the Outposts surface area: the worker CLI, the fleet API, the devin-remote binary distribution, and the spawn contract for custom orchestrators.

Authentication

Workers and orchestrators authenticate with a v3 API token belonging to a service user. The role assigned to the service user grants the token its Outposts scopes: Outposts are scoped to your account and shared across all of its organizations. devin worker start can also run without a pre-provisioned token by using your existing CLI login — see Starting without a token.

CLI

devin worker start

Polls an outpost’s queue, claims sessions, downloads the correct devin-remote binary, and serves sessions. Run it from the directory you want sessions to work in: a session’s repositories live under that directory’s repos subdirectory, so your-org/app is checked out at $(pwd)/repos/app. Repositories already present there are reused; missing ones are cloned at session start.
The worker’s environment can also carry DEVIN_CHROME_PATH to point sessions at a Chrome/Chromium binary for browser features.

Starting without a token

A pre-provisioned outposts token is not required. With no --token and no DEVIN_OUTPOSTS_TOKEN, devin worker start creates an outpost using your existing CLI login and reuses the saved worker token on later runs.

Platform validation

The worker checks that the machine’s OS matches the outpost’s platform and fails with a clear message on a mismatch, rather than repeatedly claiming and releasing queued sessions. Windows x64 machines are supported: the worker downloads the correct devin-remote binary and passes the Windows system environment through to sessions.

devin worker outpost create

Creates an outpost — a named queue of sessions served by your infrastructure. Requires the orchestrator scope.
Prints the new outpost’s ID (outpost_env-...). You can also create outposts in the web app under Settings → Environment → Outposts.

devin worker outpost delete

Deletes an outpost. Requires the orchestrator scope.

Fleet API

All endpoints live under https://api.devin.ai/opbeta/outposts/ and take a bearer token:
Resources follow a Kubernetes-style metadata / spec / status shape, and the queue follows Kubernetes list-then-watch semantics with at-least-once delivery.

Objects

Queue entry (devins)

Each queued session is represented by one queue entry: spec.network_policy reports whether the session’s network access is restricted (enabled) and the allowed destinations (allow): hostname globs ({"hostname": ...}), IPv4 addresses/CIDRs ({"ipv4": ...}), or IPv6 addresses/CIDRs ({"ipv6": ...}).

Outpost

List queued sessions

Example response:
Pagination and delivery semantics:
  • Pass each response’s cursor into the next request while has_next_page is true.
  • Delivery is at-least-once: a session at a page boundary can appear in both pages, so upsert entries by metadata.session_id rather than treating every item as new (the claim CAS makes duplicates harmless).
  • When has_next_page becomes false, save the returned cursor as the starting position for a watch.

Watch for changes

Streams Server-Sent Events. MODIFIED events fire when a session’s queue entry changes (newly queued sessions also arrive as MODIFIED); DELETED events fire when it is removed. Each SSE data field contains:
Watch semantics:
  • Persist each event’s top-level cursor after processing it; reconnect with the last persisted cursor to replay changes that occurred while disconnected.
  • Delivery is at-least-once — tolerate duplicate events.
  • Streams end after at most five minutes; a reconnecting watch loop is expected.
  • phase and acceptor_id filters 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.

Get a queue entry

Returns the queue entry for one session.

Claim a session

Atomically claims the session for the given worker identity. If another worker claimed it first, the request fails with 409. A successful claim response includes status.connect_token and status.gateway_url — the credentials devin-remote needs to connect (see the spawn contract). Claiming promises that a worker will be ready within the server-assigned claim deadline (status.claim_deadline); expired claims return to the queue automatically.

Release a claim

Releases the worker’s claim so the session returns to the queue immediately (e.g. when provisioning fails).

Outposts

Create request body:
Create, get, and delete require the orchestrator scope; each outpost response reports live status.queue_depth and status.active_claims.

Remote binary distribution

The devin worker start command automatically downloads the correct devin-remote binary. Custom orchestrators that do not use the Devin CLI can fetch it directly from:
Determine the latest version:
Download and verify:
Available platforms: If the session’s queue entry includes a spec.remote_binary_sha, use that SHA instead of latest — it pins the session to a specific tested version.

Spawn contract

If your orchestrator launches devin-remote itself instead of using devin worker start, spawn it as:
with the following environment variables: Give the remote a clean environment containing only the variables above plus basic system variables (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 you want the session to work in — repositories live under its repos subdirectory, i.e. $(pwd)/repos/<repo-name> (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_status is suspended or terminated (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 poll status.session_status while the remote runs and kill the process yourself once it reaches terminated (or the queue entry disappears).