Skip to content
Fuse
Esc
↑↓navigate↵open⌘Jpreview

Create environment

Creates a VM, uploads the manifest, starts the agent, and assigns the task. Blocks until the VM is running or creation fails. The manifest and secrets are passed inline as byte strings; a future revision may add manifest-ref indirection.

GPU requests are validated at the API boundary (not just in the CLI): negative gpus, a malformed gpu_profile, a profile with gpus < 1, or a profile on a non-MIG gpu_kind each return 400 on the first failure. gpu_profile is lowercased on the way in. A GPU request with no registered GPU host fails 503 (gpu workloads require a registered gpu host). manifest_inline must be standard base64; a bad encoding is a 400. Secrets are validated against the manifest before provisioning, and secret values are redacted from error messages.

Placement is validated at the boundary too: a malformed spec.labels key or value is a 400, and an unknown spec.host_id is a 404 before any VM is created. A pin to a real but ineligible host, and a label selector that matches no host, both fail 503 with a message naming the gate that rejected them.

POST/v1/environments
Authorization
AuthorizationBearer token · headerrequired
`Authorization: Bearer <token>` — either the static master token (`ORCH_AUTH_TOKEN`) or a revocable API key issued via /v1/api-keys (API keys require Postgres; without a database only the master token is accepted). Master-only surfaces (exec, attach, API key management) refuse API keys with 403. An expired key is refused with 401 like an unknown one. A key minted with `scopes` reaches only the hitl routes (`/v1/interventions`, `/v1/hitl/*`, and POST /v1/api-keys to mint a narrower key). Any other route is 403 code `forbidden`, reason `env_scope`, except an `/v1/environments/{vmId}` route for an environment the key does not cover, which is 404 so the key cannot probe which environments exist. **Authentication can be disabled entirely.** When neither `ORCH_AUTH_TOKEN` nor an API key store (`DATABASE_URL`) is configured — the default local-development posture — the bearer middleware becomes a pass-through: every operation this document marks as secured is reachable with no credentials, requests carry no principal, and master-only surfaces therefore admit everyone. Never run that configuration on a reachable network; set `ORCH_REQUIRE_AUTH=true` to make the orchestrator refuse to start without a master token.
or
fuse_sessionAPI key · cookierequired
HttpOnly session cookie set by POST /login for browser callers. Consulted only when no Authorization header is present.
Request body
requiredapplication/json
task_idstringrequired
Caller-assigned task identifier. Must be unique.
specobjectrequired
Hardware requirements. Nominally required, but the server does not currently reject a missing/zero spec; scheduling against registered hosts is what fails a zero-resource request.
Show properties
cpusinteger<int32>
min 0
ram_mbinteger<int32>
min 0
storage_gbinteger<int32>
min 0
gpusinteger<int32>
Count of GPU devices requested. Zero means no GPU. With gpu_profile unset, this is whole devices; with gpu_profile set, this is the count of MIG instances of that profile (decision D5).
min 0
gpu_kindstring
Optional GPU model to match (e.g. "a100"). Empty matches any kind on a host that has GPUs.
gpu_profilestring
Optional MIG profile in mig-parted vocabulary (e.g. "1g.10gb"). When set, gpus counts MIG instances of this profile rather than whole devices, and the qemu host agent attaches them via vfio-pci sysfsdev (decision D5).
imagestring
Optional named base rootfs for the provider to boot from (resolved by the host agent against its named-rootfs directory). Empty means the provider's default base image.
regionstring
host_idstring
Pins the environment to an exact host id (the Fusefile's placement.host). A pin is a hard gate, not an override: the host must still be active, run the right backend, and fit the request. An unknown host id is rejected with 404 before any VM is created.
labelsobject
Placement label selectors (the Fusefile's placement.labels). Every pair must match the target host's operator-declared labels. A selector that matches no host is rejected with 503, never queued.
huge_pagesboolean
Back guest memory with 2M pages. The host must have that much memory reserved as hugepages, or the create answers 409. Fixed for the life of the environment, and required for a lazy migrate.
max_runtime_secondsinteger<int64>
Leak-detection ceiling for this task. Zero means "use the fleet default". The orchestrator tears down tasks that exceed this age with no state transitions.
idle_timeout_secondsinteger<int64>
Destroys the environment once it has gone this long with no exec and no attach session. Zero means "use the fleet default", which is no idle expiry unless the operator configured one. Measured from the last exec or attach, not from create, and must be at least 60 (idle detection runs on the reconcile loop). In-guest CPU use and traffic on exposed ports are not observed.
manifest_inlinestring
Optional raw manifest JSON, base64-encoded. When omitted, the orchestrator uses a minimal internal manifest.
secretsobject
Resolved key/value secrets passed to the agent.
startup_scriptstring
Optional script run inside the guest at boot.
filesobject
Guest files written before `startup_script` runs, keyed by absolute guest path with base64-encoded content. This is what a Fusefile's `copy` block compiles to. A path must be absolute and clean, and must not be under `/fuse`, which holds the guest agent's manifest, secrets and credentials; the total decoded size is capped at 512 KiB and a larger request is rejected with 413. Permissions are not carried, so an executable arrives without its bit.
startup_script_timeout_secondsinteger<int64>
Bound on `startup_script`. Omit or send 0 for the orchestrator's default (30s). The script runs synchronously inside this request, so a value above the orchestrator's configured maximum (`-max-startup-script-timeout`, 55s by default) is rejected with 400 rather than clamped.
min 0
gateway_urlstring
gateway_tokenstring
exposeExposeSpec[]
Guest ports to publish as reachable endpoints; each entry surfaces on the Environment's `endpoints`.
Show properties
Array of ExposeSpec
portintegerrequired
Guest-side port to publish.
asstring
Caller-chosen label, e.g. "http".
protocolstring
Transport to publish this port on. A forwarding rule is installed per transport, so a service that speaks both needs two entries. Omitted means tcp, which is what every request written before this field existed meant.
default: "tcp"
Allowed:tcpudp
healthcheckobject
Environment-level readiness probe. Omit it for an environment with no probe, in which case `Environment.health` is never populated. It is not evaluated inside this request: create returns as soon as the VM is up, and the verdict arrives on later reads of the environment.
Show properties
httpHealthcheckHTTP
Show properties
portintegerrequired
Guest port to dial. The probe runs inside the guest, so this port needs no matching `expose` entry.
min 1 · max 65535
pathstring
Request path, which must start with a slash. Defaults to "/".
execstring[]
An argv run inside the guest. Exit status 0 passes. An argv rather than a shell string, so no argument can be reinterpreted by a shell.
interval_secondsinteger<int64>
Seconds between attempts. 0 for the guest agent default (10s).
min 0
timeout_secondsinteger<int64>
Bound on one attempt, in seconds. 0 for the guest agent default (2s). Attempts run one at a time, so a value above `interval_seconds` is rejected with 400 rather than silently stretching the interval.
min 0
retriesinteger
Consecutive failures that flip the verdict to failing. 0 for the guest agent default (3).
min 0
start_period_secondsinteger<int64>
Grace window from the first attempt during which failures are not counted. It ends for good once the probe passes once.
min 0
desktopobject
Graphical session geometry. Omit it for an environment with no desktop, in which case a desktop image keeps its baked default geometry.
Show properties
widthintegerrequired
Display width in pixels.
min 320 · max 3840
heightintegerrequired
Display height in pixels.
min 320 · max 3840
wmstring
Window manager to run. Omit to keep the image's baked default. An image that does not carry the named window manager keeps its default; ComputerDisplay.wm reports the one actually running.
Allowed:mutterxfwm4openbox
hitlobject
Human-in-the-loop policy. Omit it and hitl is off: every request is refused with 400. Forks and migrates inherit it.
Show properties
timeout_secondsinteger<int64>
Default deadline for every intervention, 10 to 86400. 0 means 600.
min 0
on_timeoutstring
How an intervention nobody decided resolves. Default reject.
Allowed:rejectallow
requestsobjectrequired
Declared request kinds; present means allowed.
Show properties
askobject
secretHitlSecretRequests
Where a secret a human types may land. The agent names one of these targets by its `env` name or `file` path; it can never pick a destination of its own. Declaring secret requests needs `TOKEN_ENCRYPTION_KEY` on the orchestrator, or the create is 400.
Show properties
targetsHitlSecretTarget[]required
min items 1 · max items 20
Show properties
Array of HitlSecretTarget
envstring
A variable the agent's later exec calls see.
matches ^[A-Za-z_][A-Za-z0-9_]*$
filestring
An absolute, clean guest path, written with mode 0600. Paths under `/fuse` are refused; fuse keeps its own credentials there.
labelstringrequired
What the human is shown first, ahead of the agent's own message. It comes from the policy, not the agent.
max length 200
takeoverHitlTakeoverRequests
Show properties
viastring[]
Allowed ways to take over: `terminal` (a recorded shell) and `desktop` (the recorded live desktop). Omitted or empty means `terminal`. `desktop` needs the create request's `desktop` block, or the create is 400.
reviewobject
Allows review requests: the agent submits a guest file or a text/diff blob, and a human approves or rejects it with comments. It has no fields.
require_actorboolean
Refuses a decision that names no person: it must come from a key pinned to an actor, or carry `Fuse-Actor` from a `hitl:act-as` key or the master token. Otherwise 403 `forbidden`, reason `actor_required`.
notifyHitlWebhook[]
Webhooks told about every audit event on this environment (see HitlNotification for the list). Deliveries are content-free and signed. Each `secret` must name an entry of the create request's `secrets`, or the create is 400.
max items 5
Show properties
Array of HitlWebhook
urlstring<uri>required
Absolute http or https url with a host and no userinfo credentials. Redirects are not followed; a 3xx counts as a failed delivery.
secretstringrequired
Name of an entry in the create request's `secrets` whose value keys the HMAC-SHA256 signature. The value never appears in the spec. It is sealed with `TOKEN_ENCRYPTION_KEY`; without that key it is held in memory only and deliveries stop after an orchestrator restart.
gatesHitlGate[]
Gates that hold, record or deny exec, lifecycle and computer actions, and the `stall` and `expiry` signals. A gate's `decide.secret` must name an entry of the create request's `secrets`, or the create is 400.
Show properties
Array of HitlGate
namestringrequired
Identifies the gate on interventions and grants.
onstringrequired
`exec`, `lifecycle` and `computer` hold the actions fuse relays. `stall` and `expiry` are signals fuse raises on its own: a stall when the agent looks stuck, an expiry ahead of an `idle_timeout` or `max_runtime` reap. They take only the matching signal fields of `match`, are `ask` or `notify` (never `deny`), and take no `grant`.
Allowed:execlifecyclecomputerstallexpiry
matchHitlGateMatchrequired
A gate's structured match. Every field set must match.
Show properties
programstring[]
Matches the basename of argv[0] after env, sudo, nice, timeout, xargs, nohup and stdbuf are stripped.
argsstring[]
Matches when these appear in argv in this order.
shellboolean
Matches any opaque shell exec (`shell`, or sh/bash -c).
actionstring[]
`lifecycle`: `destroy`, `fork`, `migrate`, `drain`, `snapshot`, `rotate-token`. `computer`: `screenshot`, `cursor_position`, `mouse_move`, `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click`, `left_click_drag`, `left_mouse_down`, `left_mouse_up`, `type`, `key`, `hold_key`, `scroll`, `wait`, `zoom`. Computer gates match action names, never coordinates.
text_containsstring[]
`computer`: matches a `type` action whose text contains any entry, ignoring case. It implies `action` `type`, so it can be combined only with `action: [type]`.
failuresinteger
`stall`: raise after this many consecutive non-zero exec exits. A zero exit resets the run, and so does raising the stall.
min 1 · max 100
quiet_secondsinteger<int64>
`stall`: raise after this long with no exec or computer call from the agent (counted from creation before the first call). One quiet spell raises one stall. A stall gate needs `failures`, `quiet_seconds` or both.
min 60 · max 86400
before_secondsinteger<int64>
`expiry`: raise this long before the `idle_timeout` or `max_runtime` reaper is due. Required for an expiry gate.
min 60 · max 86400
modestring
`ask` (the default) holds the action for a human; `notify` lets it run and records a terminal intervention; `deny` refuses it with 403 `hitl_denied`. For a `stall`, `ask` locks the agent's exec and computer calls (423) until a human acks it; for an `expiry`, `ask` holds the reaper until a human decides.
Allowed:asknotifydeny
grantstring
Default reach of an approval: `once` (the default), `env`, or `ttl:<duration>`.
timeout_secondsinteger<int64>
Overrides the policy deadline for this gate.
reasonstring
Returned on a deny and shown to the human on an ask.
decideHitlDecider
A webhook asked before a human, on an `ask` gate on `exec`, `lifecycle` or `computer` (refused on `notify`, `deny`, `stall` and `expiry`). Before the gate holds an action, fuse POSTs a HitlDeciderRequest to `url`, signed with a `Fuse-Signature` header exactly like a HitlWebhook delivery, and reads a HitlDeciderResponse back. - `allow`: the action goes on as if this gate had not matched (another matching `ask` gate still applies). It is recorded as an intervention born approved, decided by actor `decider:<gate>` with source `decider`. No grant is created. - `deny`: the call gets 403 `hitl_denied` with the decider's reason; the intervention is born rejected. - `ask`: the action is held for a human as usual, with the verdict in `payload.decider`. The call has a fixed 5s timeout (a computer action first spends up to 5s more taking the screenshot) and is not retried. Any failure (timeout, transport error, non-2xx, a redirect, which is not followed, a body that is not JSON or names an unknown decision, or a signing secret the orchestrator no longer holds) falls through to `ask`, with the error in `payload.decider.error`. The decider is not asked when a grant already covers the action.
Show properties
urlstring<uri>required
Absolute http or https url with a host and no userinfo credentials.
secretstringrequired
Name of an entry in the create request's `secrets` whose value keys the HMAC-SHA256 signature, sealed and kept like a HitlWebhook secret.
execHitlExecPolicy
Allowlist mode for exec. A program not in `allow` is gated.
Show properties
defaultstringrequired
Allowed:askdeny
allowstring[]required
Program basenames that run without a gate.
egressobject
Outbound networking policy. Omit it for direct egress, which is also what an explicit `{"mode": "direct"}` means; the two produce identical behaviour and identical stored state. An unknown `provider` is refused with 400 before any environment exists, and a backend that fails to come up is a failed create with no surviving environment.
Show properties
modestring
`direct` is NAT straight out of the host, exactly the behaviour of an environment that never mentions egress. `proxy` routes every outbound connection through a backend the orchestrator provisions, health-checks and tears down. In proxy mode the host installs no direct route at all, so a workload that ignores the proxy gets no egress rather than direct egress.
default: "direct"
Allowed:directproxy
providerstring
The backend, required for `proxy`. The orchestrator refuses a name it has not registered, and the error lists the ones it has. `mock` is an in-process socks5 backend for local development that provides no privacy or isolation.
protocolstring
How the workload talks to the proxy. Proxy mode leaves the guest with no working local DNS by design, so a client must resolve names at the proxy: `socks5h://`, or http CONNECT, which always does.
default: "socks5"
Allowed:socks5http
Responses
201Environment created.
idstringrequired
Fleet-assigned VM identifier (prefix + task_id).
statestringrequired
Allowed:provisioningrunningdrainingdestroying
task_idstringrequired
Empty when the VM is being torn down.
host_idstring
Scheduler-assigned host ID when placement uses the host registry.
urlstringrequired
host:port address where the guest agent (fused) is reachable, via per-VM DNAT. Empty before the VM is running.
specResourceSpecrequired
Hardware requirements for a VM. No lower bound is declared on cpus/ram_mb because the server does not enforce one: createEnvironment validates only task_id and the GPU fields, so a spec with `cpus: 0` / `ram_mb: 0` is accepted and scheduled. Callers should still send positive values; a strict request validator must not reject zero, or it will refuse payloads the API accepts.
Show properties
cpusinteger<int32>
min 0
ram_mbinteger<int32>
min 0
storage_gbinteger<int32>
min 0
gpusinteger<int32>
Count of GPU devices requested. Zero means no GPU. With gpu_profile unset, this is whole devices; with gpu_profile set, this is the count of MIG instances of that profile (decision D5).
min 0
gpu_kindstring
Optional GPU model to match (e.g. "a100"). Empty matches any kind on a host that has GPUs.
gpu_profilestring
Optional MIG profile in mig-parted vocabulary (e.g. "1g.10gb"). When set, gpus counts MIG instances of this profile rather than whole devices, and the qemu host agent attaches them via vfio-pci sysfsdev (decision D5).
imagestring
Optional named base rootfs for the provider to boot from (resolved by the host agent against its named-rootfs directory). Empty means the provider's default base image.
regionstring
host_idstring
Pins the environment to an exact host id (the Fusefile's placement.host). A pin is a hard gate, not an override: the host must still be active, run the right backend, and fit the request. An unknown host id is rejected with 404 before any VM is created.
labelsobject
Placement label selectors (the Fusefile's placement.labels). Every pair must match the target host's operator-declared labels. A selector that matches no host is rejected with 503, never queued.
huge_pagesboolean
Back guest memory with 2M pages. The host must have that much memory reserved as hugepages, or the create answers 409. Fixed for the life of the environment, and required for a lazy migrate.
max_runtime_secondsinteger<int64>
Leak-detection ceiling for this task. Zero means "use the fleet default". The orchestrator tears down tasks that exceed this age with no state transitions.
idle_timeout_secondsinteger<int64>
Destroys the environment once it has gone this long with no exec and no attach session. Zero means "use the fleet default", which is no idle expiry unless the operator configured one. Measured from the last exec or attach, not from create, and must be at least 60 (idle detection runs on the reconcile loop). In-guest CPU use and traffic on exposed ports are not observed.
created_atstring<date-time>required
updated_atstring<date-time>required
errorstring
Last failure message, empty on the happy path.
endpointsEndpoint[]
Published network endpoints for ports requested via the create request's `expose` list. Omitted when none.
Show properties
Array of Endpoint
asstring
Caller-chosen label from the expose request.
urlstringrequired
Reachable address, e.g. "http://203.0.113.5:41231".
protocolstring
The transport this endpoint was actually published on, which is not always what was requested: a host agent too old to know about the field publishes tcp and omits this. Read it rather than assuming the request was honoured.
Allowed:tcpudp
portintegerrequired
The guest-side port this endpoint publishes.
healthobject
Last verdict of the environment-level healthcheck. Omitted when the environment declared no healthcheck, and omitted until the first verdict has been read back from the guest. The orchestrator refreshes it on the reconcile tick (30s by default), so it lags the guest by up to one tick.
Show properties
statestringrequired
The probe's last verdict. Deliberately separate from `Environment.state`: an unhealthy environment is still a running one, and nothing tears it down for a failing probe.
Allowed:startingpassingfailing
sincestring<date-time>
When the probe entered this state.
failuresinteger
Consecutive failed attempts. Zero while passing.
messagestring
The last attempt's failure detail. Empty while passing.
egressobject
The resolved outbound policy. Always present on a server that knows about egress: an environment created without asking reports `direct`, so a caller can tell "not proxied" from "this server predates the field".
Show properties
modestringrequired
Allowed:directproxy
providerstring
The backend serving this environment. Proxy only.
protocolstring
Proxy only.
Allowed:socks5http
endpointstring
The proxy url the workload was handed, e.g. `socks5h://10.200.3.1:1080`. A host-local address useless to anyone not already inside the sandbox, and never a credential. Proxy only.
healthobject
The endpoint's last probe verdict. Proxy only, and `unknown` until the reconcile tick (30s by default) has probed it once. An unhealthy proxy reports and nothing acts on it: the environment keeps running with no egress, never with direct egress.
Show properties
statestringrequired
Allowed:unknownhealthyunhealthy
reasonstring
The backend-level failure, e.g. `dial tcp 10.200.3.1:1080: connection refused`. Empty while healthy. Never a destination hostname or anything drawn from the sandbox's traffic.
checked_atstring<date-time>
400Malformed request.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
404Resource does not exist. Note the distinct code `route_not_found`, returned when the URL matches no registered route at all (wrong host, port, or path prefix) rather than a known route with a missing resource.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
409Conflicts with current state (e.g. duplicate task assignment).
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
500Unexpected server error.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
503No registered host has room for this spec — the scheduler found no capacity, or no hosts are registered at all (code `unavailable`). This includes the GPU case: a spec asking for GPUs a host cannot satisfy lands here. There is no shutdown-rejection path; a 503 always means placement failed, so it is worth retrying once capacity frees up.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
Request
curl -X POST "http://localhost:8080/v1/environments" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "task_id": "string",
  "spec": {
    "cpus": 0,
    "ram_mb": 0,
    "storage_gb": 0,
    "gpus": 0,
    "gpu_kind": "string",
    "gpu_profile": "string",
    "image": "string",
    "region": "string",
    "host_id": "string",
    "labels": {},
    "huge_pages": true,
    "max_runtime_seconds": 0,
    "idle_timeout_seconds": 0
  },
  "manifest_inline": "string",
  "secrets": {},
  "startup_script": "string",
  "files": {},
  "startup_script_timeout_seconds": 0,
  "gateway_url": "string",
  "gateway_token": "string",
  "expose": [
    {
      "port": 0,
      "as": "string",
      "protocol": "tcp"
    }
  ],
  "healthcheck": {
    "http": {
      "port": 0,
      "path": "string"
    },
    "exec": [
      "string"
    ],
    "interval_seconds": 0,
    "timeout_seconds": 0,
    "retries": 0,
    "start_period_seconds": 0
  },
  "desktop": {
    "width": 0,
    "height": 0,
    "wm": "mutter"
  },
  "hitl": {
    "timeout_seconds": 0,
    "on_timeout": "reject",
    "requests": {
      "ask": {},
      "secret": {
        "targets": [
          {
            "env": "string",
            "file": "string",
            "label": "string"
          }
        ]
      },
      "takeover": {
        "via": [
          "terminal"
        ]
      },
      "review": {}
    },
    "require_actor": true,
    "notify": [
      {
        "url": "<uri>",
        "secret": "string"
      }
    ],
    "gates": [
      {
        "name": "string",
        "on": "exec",
        "match": {
          "program": [
            "string"
          ],
          "args": [
            "string"
          ],
          "shell": true,
          "action": [
            "string"
          ],
          "text_contains": [
            "string"
          ],
          "failures": 0,
          "quiet_seconds": 0,
          "before_seconds": 0
        },
        "mode": "ask",
        "grant": "string",
        "timeout_seconds": 0,
        "reason": "string",
        "decide": {
          "url": "<uri>",
          "secret": "string"
        }
      }
    ],
    "exec": {
      "default": "ask",
      "allow": [
        "string"
      ]
    }
  },
  "egress": {
    "mode": "direct",
    "provider": "string",
    "protocol": "socks5"
  }
}'
Response
{
  "id": "string",
  "state": "provisioning",
  "task_id": "string",
  "host_id": "string",
  "url": "string",
  "spec": {
    "cpus": 0,
    "ram_mb": 0,
    "storage_gb": 0,
    "gpus": 0,
    "gpu_kind": "string",
    "gpu_profile": "string",
    "image": "string",
    "region": "string",
    "host_id": "string",
    "labels": {},
    "huge_pages": true,
    "max_runtime_seconds": 0,
    "idle_timeout_seconds": 0
  },
  "created_at": "2024-01-01T00:00:00Z",
  "updated_at": "2024-01-01T00:00:00Z",
  "error": "string",
  "endpoints": [
    {
      "as": "string",
      "url": "string",
      "protocol": "tcp",
      "port": 0
    }
  ],
  "health": {
    "state": "starting",
    "since": "2024-01-01T00:00:00Z",
    "failures": 0,
    "message": "string"
  },
  "egress": {
    "mode": "direct",
    "provider": "string",
    "protocol": "socks5",
    "endpoint": "string",
    "health": {
      "state": "unknown",
      "reason": "string",
      "checked_at": "2024-01-01T00:00:00Z"
    }
  }
}