Environment object
Create, get, list, and update endpoints return this structure.
| Field | Type | Description |
|---|
id | string | Environment ID with the env_ prefix |
type | string | Always "environment" |
name | string | Environment name, up to 255 characters. It must not be empty after trimming leading and trailing whitespace |
description | string | Environment description |
config | Environment config | Normalized Environment configuration. Missing package-manager fields are added |
metadata | object | Environment metadata; defaults to {} when omitted |
archived_at | string | null | Archive time in RFC 3339 format; null while active |
created_at | string | Creation time in RFC 3339 format |
updated_at | string | Last update time in RFC 3339 format |
identity_id | string | null | Owning Forward identity. Returns the Identity ID for an Identity-owned resource, or null otherwise. See Identity ownership. |
icon_url | string | null | Icon URL associated by Forward |
binding_info | Binding info | Binding information, such as Template reference counts |
Identity ownership
An account (or Workspace) can have multiple Identities. Each Identity represents an end user of the product integrated with that account (or Workspace).
An Environment can belong to the account (or Workspace) or to an Identity. Ownership determines who can see and operate it.
Selecting ownership
| Caller | Owner | How to select |
|---|
| PAT | Account / Workspace | Omit identity_id (the default, unchanged behavior). |
| PAT | A specific Identity | Pass the query parameter identity_id=<identity_id>. |
| Admin SAT | Workspace | Resolved automatically; parameters cannot switch ownership. |
| Identity-bound SAT | The bound Identity | Resolved automatically; parameters cannot switch ownership. |
identity_id is optional and is used only for Identity-owned resources. A PAT can specify it explicitly; omitting it uses the administrator scope. With SAT, issue an Identity-scoped token and do not explicitly pass this parameter, even with an empty value. Doing so returns HTTP 400.
An Identity specified with a PAT must belong to the account or Workspace represented by that PAT and must be enabled. A nonexistent, disabled, deleted, or foreign Identity returns 404.
Ownership isolation
- Calls in the account or Workspace scope cannot see Identity-owned Environments.
- An Identity cannot see Environments owned by the account (or Workspace) itself or by other Identities in the same account.
- In a valid Identity scope (a PAT with a valid
identity_id, or an Identity SAT), cross-scope get-by-ID, update, archive, and delete operations return 404, without distinguishing a nonexistent resource from a resource owned by someone else.
- PAT calls without
identity_id and Admin SAT calls retain the existing behavior: an Owner mismatch returns 403.
Supported endpoints
Create, search, list, get, update, archive, and delete Environment endpoints support the identity_id query parameter.
GET /api/v1/forward/resources/batch does not yet support identity_id. Its visibility rules are unchanged.
Environment config
Environment runtime configuration. When omitted, it defaults to {"type":"cloud"}. When provided explicitly, it must not be null or an empty object.
self_hosted config
A self_hosted Environment accepts only type and an optional setup_script and startup_script. Supplying packages or another unsupported field returns 400 invalid_request_error. For self_hosted, startup_script is only stored and returned as is; whether to run it is up to the external worker.
{"type": "self_hosted", "setup_script": "./initialize-worker.sh"}
cloud config response shape
A cloud config response includes every reserved package-manager array under packages; undeclared arrays are returned as []. For example:
{
"type": "cloud",
"packages": {
"type": "packages",
"apt": [],
"cargo": [],
"gem": [],
"go": [],
"npm": [],
"pip": []
}
}
Environment storage
storage.buckets[] declares your own OSS buckets to mount at /mnt/buckets/<name> in the sandbox. Only cloud config is supported, with a maximum of 7 buckets. For full details (credentials, authorization, permission policies, and sandbox behavior), see Mount your own OSS buckets (Beta).
| Field | Type | Required | Description |
|---|
name | string | Yes | Mount directory name, unique within the Environment |
provider | string | Yes | Always "aliyun-oss" |
region | string | Yes | OSS region ID, such as cn-hangzhou |
bucket | string | Yes | Bucket name |
prefix | string | No | Mount only this prefix; omit to mount the entire bucket |
access | string | No | read_only (default) or read_write |
accelerator_endpoint | string | No | <label>.oss-data-acc.aliyuncs.com |
credential | object | Yes | {"type":"ram_role","role_arn":"..."} or {"type":"access_key","access_key_id":"...","access_key_secret":"..."} |
credential is write-only: responses return only type; for access_key, they also return a masked access_key_id (such as LTAI****sKey). Omitting this field during an update or returning the masked values unchanged preserves the saved credentials.
{
"type": "cloud",
"storage": {
"buckets": [
{
"name": "datasets",
"provider": "aliyun-oss",
"region": "cn-hangzhou",
"bucket": "my-bucket",
"prefix": "team/data/",
"access": "read_only",
"credential": {"type": "ram_role", "role_arn": "acs:ram::1234567890123456:role/qoder-oss-mount"}
}
]
}
}
Environment packages
packages maps package-manager names to arrays of package specification strings. apt, npm, and pip currently install packages. cargo, gem, and go are reserved response fields and cannot currently install dependencies here.
| Key | Type | Description | Example |
|---|
type | string | Always "packages" in responses; do not supply it in requests | "packages" |
apt | array of string | Debian/Ubuntu package declarations, installed with apt-get install -y | ["git", "curl"] |
npm | array of string | Global Node.js package declarations, installed with npm install -g | ["pnpm@9"] |
pip | array of string | Python package declarations, installed with pip install | ["PyYAML==6.0.1"] |
cargo | array of string | Reserved response field; dependency installation through this field is not currently supported | [] |
gem | array of string | Reserved response field; dependency installation through this field is not currently supported | [] |
go | array of string | Reserved response field; dependency installation through this field is not currently supported | [] |
Every value must be an array of strings. Undeclared package-manager arrays are returned as [].
Environment setup script
setup_script is a shell script run during sandbox preparation after packages installation. Use it for initialization that cannot be expressed with packages, such as cloning code, writing configuration files, or warming caches.
| Constraint | Value |
|---|
| Type | string |
| Max length | 64 KB |
| Interpreter | /bin/bash -lc |
| When it runs | Sandbox preparation, after packages installation |
| Timeout | 10 minutes |
| Nonzero exit | Fails preparation of the current sandbox |
| Repeated execution | Does not run again after succeeding in the same sandbox; runs again after the sandbox is reclaimed and rebuilt |
{
"config": {
"type": "cloud",
"setup_script": "set -euo pipefail\n[ -d /workspace/.git ] || git clone https://github.com/me/repo /workspace\ncd /workspace && pnpm install --frozen-lockfile"
}
}
Environment startup script
startup_script is a shell script run on every start of the sandbox: on the first start it runs after setup_script, and it runs again each time the sandbox resumes from a pause. A resumed sandbox keeps the files on its disk but not its processes. Put installation steps in setup_script, and start databases, dev servers, and other processes in startup_script.
| Constraint | Value |
|---|
| Type | string |
| Max length | 64 KB |
| Interpreter | /bin/bash -lc, with the same working directory and environment variables as setup_script |
| When it runs | After each start of the sandbox, before the first tool runs; on the first start, after setup_script |
| Timeout | 5 minutes; at the timeout, the script is stopped together with the background processes it started |
| Repeated execution | At most once per start (recorded before execution begins); runs again on the next start |
| Failure | Does not fail the Session; each start in which the script fails produces one session.error with error.type environment_startup_failed_error, with no retry within the same start |
The script must exit: run long-lived processes in the background with their output redirected to a file (for example nohup pnpm dev > /tmp/dev.log 2>&1 &). On a resume, previous processes may still be running (some runtimes keep memory across a pause), so the script must check whether the service is already running.
{
"config": {
"type": "cloud",
"packages": {"apt": ["redis-server"]},
"startup_script": "pgrep -x redis-server || redis-server --daemonize yes"
}
}
Custom Environment metadata key-value pairs. created_by is reserved by Forward and must not be supplied by callers (supplying it returns 400).
| Constraint | Value |
|---|
| Caller-supplied limit | Typically 15 custom key-value pairs |
| Maximum key length | 64 characters |
| Value type | string |
| Maximum value length | 512 characters |
Binding info
Reference summary included by Forward in Environment responses.
| Field | Type | Description |
|---|
agent_template_count | integer | Number of Templates currently bound to the Environment |
| Field | Type | Description |
|---|
data | array of Environment objects | Records on the current page |
has_more | boolean | Whether another page is available |
next_page | string | null | Forward cursor for the next page (recommended). Equals the current page's last_id when has_more=true; otherwise null |
first_id | string | null | ID of the first record on the current page |
last_id | string | null | ID of the last record on the current page |
The request cursor parameters page, after_id, and before_id are mutually exclusive; providing more than one returns 400. Use page where possible; it has the same semantics as after_id.