> ## Documentation Index
> Fetch the complete documentation index at: https://docs.envzero.com/llms.txt
> Use this file to discover all available pages before exploring further.

# env zero CLI command reference

> Every env zero CLI command and flag, with authentication and stored context, the JSON output envelope, pagination, exit codes, and telemetry.

This page covers env zero CLI version 2.7.0. Run `env0 <command> --help` to print the flags of the version you have installed. To install the CLI and run a first deployment, see [env zero CLI](https://docs.envzero.com/guides/community-and-resources/support-and-help/env-zero-cli.md).

## Global flags and environment variables

Every command accepts these flags:

| Flag | Environment variable | Default | Description |
| :- | :- | :- | :- |
| `--api-key-id` | `ENV0_API_KEY_ID` | none | API key id |
| `--api-key-secret` | `ENV0_API_KEY_SECRET` | none | API key secret |
| `--api-url` | `ENV0_API_URL` | `https://api.env0.com` | env zero API host |
| `-o`, `--output` | none | `json` | Output format: `json`, `compact`, or `table` |

The CLI also reads these environment variables:

| Variable | Description |
| :- | :- |
| `ENV0_ORGANIZATION_ID` | Organization, by id or name, for commands that take `--organization` |
| `ENV0_PROJECT_ID` | Project for commands that take `--project` |
| `ENV0_CONFIG_HOME` | Directory for stored context. The CLI writes to an `env0` folder inside it. Defaults to your OS user config directory. |
| `ENV0_TELEMETRY_DISABLED`, `DO_NOT_TRACK` | Turn off [telemetry](#telemetry) |
| `ENV0_CLIENT` | Tag that telemetry reports instead of `cli`. Set it when you embed the CLI in another tool. |

Each value resolves in this order: flag, then environment variable, then stored context, then default. In CI and agents, set the environment variables so each run is explicit and does not depend on stored state.

Anywhere a command takes an organization (`--organization`, `ENV0_ORGANIZATION_ID`, or the argument of `organization use` and `organization get`), you can pass the organization's name instead of its id. The name must match exactly one organization your credentials can see, ignoring case and extra spaces. Several matches exit `2` and list their ids, no match exits `4`, and a name made only of spaces exits `64`. A value shaped like a UUID is always treated as an id. A name costs one extra request to list your organizations, so scripts should pass ids.

## Sign-in and stored context

`env0 login` stores credentials and a default host, organization, and project, so later commands need no flags.

* **Browser sign-in.** With no API key set and a terminal attached, `login` opens a browser sign-in and asks you to confirm a short code. `--no-browser` prints the URL instead.
* **API key.** When `--api-key-id` and `--api-key-secret` (or `ENV0_API_KEY_ID` and `ENV0_API_KEY_SECRET`) are set, `login` validates and stores that key.
* **No key and no terminal.** `login` exits `3`. CI and agents should set the API key environment variables and skip `login`.

On a terminal, `login` then asks you to pick an organization and project, and the `--organization` and `--project` flags skip the matching picker. Without a terminal, it stores the `--organization` and `--project` you pass.

Credentials are stored in the OS keychain. If the keychain write fails, the CLI writes them to a plain-text file readable only by your user, prints a `warning:` line on stderr naming the file, and still exits `0`. `env0 logout` removes that file.

Credentials are scoped to the API host they were issued for. Signing in to a second host does not sign you out of the first, and a host you have not signed in to exits `3` even when another host is authenticated. The host, organization, and project are stored once, in `config.json`, so switching hosts means running `login` again.

Browser sign-in works out which env zero tenant to use from the API host. For a host outside `env0.com`, it exits `64` unless you pass `--tenant`. API keys work against any host.

## Output

Every command prints one JSON envelope, except `env0 skill show` (raw Markdown), `env0 deployment logs --follow` (a [log stream](#deployment-logs)), and [`env0 api`](#api) (the raw response body). Data goes to stdout and errors go to stderr:

```json theme={null}
{ "apiVersion": "0.1", "data": { } }
```

```json theme={null}
{ "apiVersion": "0.1", "error": { "type": "not_found", "message": "...", "hint": "...", "host": "https://api.env0.com" } }
```

* `-o json` prints indented JSON. `-o compact` prints the same JSON on one line, for `jq`. `-o table` prints aligned columns.
* On error, stdout is empty and the exit code is non-zero, so `env0 ... -o compact | jq .data` is safe.
* Arrays are always arrays. An empty result is `[]`, never `null` or a missing key.
* `error.host` names the host the CLI was pointed at. It is absent for `usage` errors and for failures before a host is chosen. Credentials and paths are removed from it.

### Pagination

`environment list`, `deployment list`, and `plan list` return at most 100 rows by default and add a `page` object next to `data`:

```json theme={null}
{ "apiVersion": "0.1", "data": [ ], "page": { "truncated": true, "nextOffset": 100 } }
```

`"truncated": true` means more rows exist. Pass `--all` to fetch every page. `--limit` accepts 1 to 100.

`organization list`, `project list`, and `template list` are not paginated. They return the whole collection and take no `--limit` or `--all`.

## Exit codes

| Code | `error.type` | Meaning |
| :- | :- | :- |
| `0` | none | Success |
| `1` | `unknown` | Unexpected error, a `--wait` or `--follow` deployment that ended in a failed state, or an [`environment apply`](#environment-apply) or [`environment destroy -f`](#environment-destroy) in which any environment failed |
| `2` | `user_input`, `conflict` | The backend rejected well-formed input, or the resource is in a conflicting state |
| `3` | `auth` | Missing credentials, or no permission for the operation or resource |
| `4` | `not_found` | The resource does not exist |
| `64` | `usage` | Wrong invocation: unknown command or flag, missing or conflicting arguments, `--limit` outside 1 to 100, `--tail` below 1 or an unknown `--step` on `deployment logs`, or an unplaceable sign-in host |
| `75` | `transient` | Retry later. Also returned when `--wait` hits its `--timeout` before the deployment finishes. |

With `--wait` or `deployment logs --follow`, the exit code reflects the deployment's final status:

| Final status | Exit code |
| :- | :- |
| `SUCCESS`, `SKIPPED`, `WAITING_FOR_USER` | `0` |
| `FAILURE`, `INTERNAL_FAILURE`, `TIMEOUT`, `CANCELLED`, `ABORTED` | `1` |

The deployment is printed to stdout in both cases, including `failedCommand` and `errorMessage` when it failed. `WAITING_FOR_USER` means the deployment needs approval, not that it failed.

## context

```bash theme={null}
env0 context <environment-id> [--deployments <n>] [--inventory]
```

Returns an environment's state, recent deployments, and a failure summary in one call. Read `.data.summary` first, then `.data.deployments`, then [`deployment logs`](#deployment-logs) for the full error.

| Flag | Default | Description |
| :- | :- | :- |
| `--deployments` | `5` | Number of recent deployments to include, 1 to 100 |
| `--inventory` | off | Add an `inventory` block with the cloud resources behind the environment. See [Cloud inventory](#cloud-inventory). |

The output has these keys:

* `environment`: the same object `environment get` returns.
* `deployments`: recent deployments, newest first.
* `summary`: drift status and the latest deployment's status and type. When that deployment failed (`FAILURE`, `INTERNAL_FAILURE`, or `TIMEOUT`), it also carries `failedCommand`, a truncated `errorMessage`, and `exitCode`. It carries `costEstimation` when the environment has one.
* `subEnvironments`: for a workflow environment, each sub-environment's alias, plus the revision and status of its latest deployment.
* `inventory`: only with `--inventory`. It holds `resources` (the count of cloud resources the environment manages), `unmanaged` (the count of resources [Cloud Compass](#cloud-inventory) attributes to the environment that its state does not hold), `drifted` (the count of drifted resources), `resourceTypes` (a list with the managed and drifted counts for each resource type), and `insights` (CloudQuery insight counts by severity on those resources). `resources` is absent when Cloud Compass cannot read the environment's managed state. `drifted` and the drift in `resourceTypes` are absent when drift detection is off for the environment or Cloud Compass cannot read drift. A Cloud Compass problem never fails `context`: with no connection the block is `{"connected": false, "reason": "..."}`, and on any other error it carries only `error`.

## Organizations

### organization list

```bash theme={null}
env0 organization list
```

Lists every organization you can access. `org` is an alias for `organization`.

### organization get

```bash theme={null}
env0 organization get <organization-name-or-id>
```

### organization use

```bash theme={null}
env0 organization use [<organization-name-or-id>]
```

Stores a default organization for later commands. Takes an organization name or id. Without one on a terminal, it shows a picker you can type to filter.

## Projects

### project list

```bash theme={null}
env0 project list [--organization <organization-name-or-id>]
```

Lists every project in the organization.

### project get

```bash theme={null}
env0 project get <project-id>
```

### project create

```bash theme={null}
env0 project create --name <name> [--organization <organization-id>] [flags]
```

| Flag | Description |
| :- | :- |
| `--name` | Project name. Required. |
| `--organization` | Organization to create the project in |
| `--description` | Project description |
| `--parent` | Parent project id, to nest the new project under it |
| `--tag` | Project tag. Repeatable. |

The new project does not become your stored project. Pass its id (`.data.id`) as `--project` to later commands, or run `env0 project use <project-id>`. Requires permission to create projects in the organization.

### project archive

```bash theme={null}
env0 project archive <project-id>
```

Archives the project. `project get` and `project list` still return it with `isArchived: true`. The CLI cannot unarchive a project, and [archiving a project cannot be undone](https://docs.envzero.com/guides/admin-guide/projects.md#archive-a-project).

### project use

```bash theme={null}
env0 project use [<project-name-or-id>]
```

Stores a default project, and its organization, for later commands. Without an argument on a terminal, it shows a picker of the stored organization's projects that you can type to filter.

A name, or a `Parent / Child` path, is looked up in the current organization and must match exactly, ignoring case. A full path wins over a nested project's own name, so `web` picks a top-level `Web` before `Platform / Web`. Archived projects are skipped. Several matches exit `2` and list their ids. No match exits `4` and names similar projects.

## Templates

### template list

```bash theme={null}
env0 template list (--organization <organization-id> | --project <project-id>)
```

Lists templates in exactly one scope. Passing both flags, or neither with no stored context, exits `64`.

### template get

```bash theme={null}
env0 template get <template-id>
```

### template create

```bash theme={null}
env0 template create --name <name> --repository <url> --type <type> [flags]
```

| Flag | Description |
| :- | :- |
| `--name` | Template name. Required. |
| `--repository` | Git repository URL. Required. A trailing `.git` is removed. |
| `--type` | IaC type, for example `terraform`, `opentofu`, `terragrunt`, or `workflow`. Required. |
| `--organization` | Organization to create the template in. Required unless stored. |
| `--project` | Project to assign the new template to. Defaults to the stored project. |
| `--path` | Path to the IaC code in the repository |
| `--revision` | Default branch or tag |
| `--iac-version` | IaC tool version. Terraform defaults to `RESOLVE_FROM_TERRAFORM_CODE`, OpenTofu to `latest`. |
| `--vcs-connection-id` | [VCS connection](#vcs-list) for a private repository |
| `--github-installation-id` | GitHub App installation id for a private repository |
| `--token-id` | VCS token id for a private repository |
| `--gitlab` | The repository is hosted on GitLab |

Requires permission to manage templates in the organization.

### template assign

```bash theme={null}
env0 template assign <template-id> --project <project-id>
```

Assigns a template to a project. `environment create --template` only deploys templates assigned to the target project.

### template delete

```bash theme={null}
env0 template delete <template-id>
```

Removes the template from `template list`. `template get` can still return the record.

## Environments

`env` is an alias for `environment`. Actions take an environment id, not a name.

### environment list

```bash theme={null}
env0 environment list [flags]
```

| Flag | Description |
| :- | :- |
| `--project` | Filter by project |
| `--organization` | Filter by organization name or id |
| `--name` | Filter by exact name. Not a substring match. |
| `--tag` | Filter by `KEY` or `KEY=VALUE`. Repeatable. An environment matches if it carries any of the tags. |
| `--limit` | Maximum rows, 1 to 100 |
| `--all` | Fetch every page |

All filters are optional and combine. The list does not include workflow sub-environments. To see them, run [`context`](#context) on the workflow environment.

### environment get

```bash theme={null}
env0 environment get <environment-id>
```

The output always includes `tags`, as `{}` when the environment has none.

### environment create

```bash theme={null}
env0 environment create --name <name> --project <project-id> (--template <template-id> | --repository <url>) [flags]
```

Creates an environment and starts its first deployment. The output is that deployment. With `--wait`, its `environmentId` is the new environment's id.

| Flag | Description |
| :- | :- |
| `--name` | Environment name. Required. |
| `--project` | Project to create the environment in. Required unless stored. |
| `--template` | Template to deploy. Use this or `--repository`. |
| `--repository` | Git repository to deploy without a template. A trailing `.git` is removed. Requires `--type` and `--organization`. |
| `--type` | IaC type for `--repository`, for example `terraform`, `opentofu`, `terragrunt`, or `workflow` |
| `--organization` | Organization. Required with `--repository`. |
| `--path` | Path to the IaC code in the repository, with `--repository` |
| `--revision` | Branch or tag to deploy |
| `--iac-version` | IaC tool version, with `--repository`. Terraform defaults to `RESOLVE_FROM_TERRAFORM_CODE`, OpenTofu to `latest`. |
| `--vcs-connection-id` | [VCS connection](#vcs-list) for a private repository |
| `--github-installation-id` | GitHub App installation id for a private repository |
| `--token-id` | VCS token id for a private repository |
| `--gitlab` | The repository is hosted on GitLab |
| `--workspace-name` | Terraform workspace name. Set once at creation and cannot be changed. |
| `--var` | Environment variable as `KEY=VALUE`. Repeatable. |
| `--secret` | Sensitive environment variable as `KEY=VALUE`. Repeatable. |
| `--tf-var` | Terraform variable as `KEY=VALUE`. Repeatable. |
| `--requires-approval` | Require approval before each deployment, including the first |
| `--ttl` | Lifespan as a duration (`2h`) or RFC 3339 timestamp, after which env zero destroys the environment. The minimum is `1h`. See [environment ttl](#environment-ttl-set). |
| `--tag` | Tag as `KEY=VALUE`. Repeatable. Repeat a key to give it several values. |
| `--comment` | Deployment comment |
| `-f`, `--manifest` | Workflow manifest file, with `--template`. See [Deploy workflows with the CLI](https://docs.envzero.com/guides/community-and-resources/support-and-help/env-zero-cli/workflows.md). |
| `--wait` | Wait for the deployment to finish. See [Waiting for a deployment](#waiting-for-a-deployment). |
| `--quiet` | With `--wait`, do not stream logs to stderr |
| `--poll-interval` | How often `--wait` polls. Default `5s`. |
| `--timeout` | How long `--wait` waits. Default `1h`. |

A `--repository` environment gets a single-use template that needs no cleanup. When no [cloud credential](https://docs.envzero.com/guides/getting-started/connect-your-cloud-account.md) is assigned to the project, pass provider credentials as `--secret`, for example `--secret AWS_ACCESS_KEY_ID=...`. An assigned credential keeps keys out of your shell history and process list.

### environment deploy

```bash theme={null}
env0 environment deploy <environment-id> [flags]
```

Starts a deployment and prints it. A deployment started while another is running is queued, not rejected.

| Flag | Description |
| :- | :- |
| `--revision` | Branch or tag to deploy |
| `--target` | Resource target. Repeatable. |
| `--var`, `--secret`, `--tf-var` | Variables, as on `environment create`. Repeatable. |
| `--requires-approval` | Require approval for this deployment. `--requires-approval=false` skips it. Omit to keep the environment's setting. |
| `--comment` | Deployment comment |
| `-f`, `--manifest` | Workflow manifest file |
| `--node` | Deploy only this workflow sub-environment, by alias |
| `--run-from-here` | With `--node`, also deploy every sub-environment downstream of it |
| `--wait`, `--quiet`, `--poll-interval`, `--timeout` | As on `environment create` |
| `--pretty` | Print a readable summary and step headings instead of JSON. Cannot be combined with `-o`. |

`--manifest`, `--node`, and `--run-from-here` are covered in [Deploy workflows with the CLI](https://docs.envzero.com/guides/community-and-resources/support-and-help/env-zero-cli/workflows.md).

### environment plan

```bash theme={null}
env0 environment plan <environment-name-or-id> [flags]
```

Previews what a deployment would change without applying anything. It starts a dry run, which is a plan-only deployment, always waits for it, streams its logs to stderr, and prints the finished deployment to stdout. The deployment's `type` is `dryRun`, and its `planSummary` counts the planned changes.

| Flag | Description |
| :- | :- |
| `--project` | Project to look the environment name up in. Defaults to `ENV0_PROJECT_ID` or the stored project. |
| `--quiet` | Do not stream the plan's logs to stderr |
| `--poll-interval` | How often the command polls. Default `5s`. |
| `--timeout` | How long the command waits. Default `1h`. |

* Pass an environment id or a name. A value shaped like a UUID is treated as an id. A name is looked up as `environment list --name` looks it up, except that archived environments are skipped.
* Names are not unique within a project, so the CLI never guesses. No match exits `4`. Several matches exit `2` and list each match's id and status. An empty name exits `64`.
* An archived environment is refused with exit `2`, because a dry run would unarchive it.
* The exit code follows the dry run as `--wait` does: `0` when it finished, `1` when the plan failed (read `failedCommand` and `errorMessage`), and `75` when `--timeout` runs out while the dry run may still be running.
* If the environment already has a deployment waiting for approval, the dry run queues behind it. The CLI says so on stderr, even with `--quiet`.
* A dry run that stops at `WAITING_FOR_USER` while the command waits has finished its plan. The CLI cancels it so nobody can approve it, prints it with status `CANCELLED`, and exits `0`.
* For a workflow environment, the workflow's own dry run has no steps and no `planSummary`. Each sub-environment runs its own dry run. When the workflow's dry run ends, stderr lists each sub-environment by alias with its status, plan counts, and deployment id. Read a sub-environment's full plan with [`deployment logs`](#deployment-logs).

### environment apply

```bash theme={null}
env0 environment apply -f <file> [flags]
```

Creates or deploys every environment a YAML or JSON file lists, in parallel. Use it to bring up a set of environments, such as a test workspace made of several workflow environments.

| Flag | Description |
| :- | :- |
| `-f`, `--file` | File that lists the environments. Required. |
| `--project` | Project for entries that do not set one. Defaults to `ENV0_PROJECT_ID` or the stored project. |
| `--only-failed` | Skip environments whose latest deployment did not fail, and rerun only the failed sub-environments of a failed workflow |
| `--wait` | Poll every started deployment until it reaches a final status |
| `--poll-interval` | How often `--wait` polls. Default `5s`. |
| `--timeout` | How long `--wait` waits for each deployment. Default `1h`. |

Each entry is a deploy manifest, with the same `revision`, `variables`, and `subEnvironments` as [`--manifest`](https://docs.envzero.com/guides/community-and-resources/support-and-help/env-zero-cli/workflows), plus these fields:

| Field | Description |
| :- | :- |
| `name` | Environment name. Required. |
| `template` | Template to create the environment from. Used only when the environment does not exist yet. |
| `project` | Project id. Defaults to `--project`, `ENV0_PROJECT_ID`, or the stored project. |
| `requiresApproval` | Approval gate for every create and deploy of this entry. Omit it to keep the environment's setting, or the project's default for a new environment. |
| `ttl` | Lifespan, as with [`environment create --ttl`](#environment-create). Applies only on create. |
| `tags` | Tags as `KEY=VALUE`. Repeat a key for several values. Applies only on create. |

```yaml theme={null}
environments:
  - name: payments-test
    template: <workflow-template-id>
    requiresApproval: false
    ttl: 8h
    tags: [team=payments, purpose=test]
    variables:
      - name: DISABLE_REWARDS
        value: "false"
  - name: wallet-test
    template: <workflow-template-id>
    subEnvironments:
      wallet-api:
        revision: feature/new-limits
```

```bash theme={null}
env0 environment apply -f workspace.yaml --wait -o compact
env0 environment apply -f workspace.yaml --only-failed --wait -o compact
```

* An environment is found by its exact name in its project, and case counts. Archived environments are ignored. If none exists, `apply` creates it from `template`. If one exists, `apply` redeploys it with the entry's revision, variables, and sub-environment overrides. Two live environments with the same name exit `2`.
* Most checks run before anything changes. An unknown field, an entry listed twice, a missing `template` for an environment that does not exist yet, a bad `ttl` or tag, or sub-environment overrides for an environment that is not a workflow exit `64`, and nothing is created or deployed. A wrong sub-environment alias gets the same result only where `apply` can read the workflow's aliases up front, which is an existing workflow environment that has already read its workflow file. For a new workflow environment, or an existing one that has never read its workflow file because its first deployment failed early or has not reached it, `apply` checks aliases only after it creates the environment or starts the deployment. A wrong alias there becomes that environment's `error` with type `usage`, the command exits `1`, and the other environments still deploy.
* env zero rejects approval on workflow environments. In a project that requires approval by default, a workflow entry needs `requiresApproval: false` to be created.
* Environments run in parallel, each as its own deployment. Inside a workflow, `needs:` still orders the sub-environments.
* With `--only-failed`, an environment is skipped unless it does not exist, has never been deployed, or its latest deployment ended in `FAILURE`, `INTERNAL_FAILURE`, `TIMEOUT`, `CANCELLED`, or `ABORTED`. In a failed workflow environment, when every failed sub-environment is one sub-environment or downstream of it, only that part runs again, as a `--run-from-here` run. Otherwise the whole workflow is redeployed, for example when failures start in two or more sub-environments that do not depend on each other, or when the entry sets a top-level `revision`.
* `--wait` does not stream logs, because the deployments run in parallel. Follow one with `deployment logs <deployment-id> --follow`.

The output lists every entry in file order, with these fields:

| Field | Description |
| :- | :- |
| `name`, `projectId`, `environmentId` | The entry and the environment it resolved to |
| `action` | `create`, `deploy`, `retry`, or `skip` |
| `nodes` | For a `retry`, the sub-environment it reran from |
| `deployments` | Each deployment the entry started, in its final state with `--wait` |
| `error` | `type` and `message`, when a request for that environment failed |

One environment failing does not stop the others. The exit code is `0` when every environment started and, with `--wait`, none ended in a failed state. It is `1` when any environment has an `error` or a failed deployment, and the full result is still printed. A `--wait` that times out is that environment's `error`, with type `transient`.

### environment destroy

```bash theme={null}
env0 environment destroy (<environment-id> | -f <file>) --yes [flags]
```

Destroys the environment's resources. This cannot be undone, so it requires `--yes` (or `--force`) and has no prompt. On an environment that requires approval, the destroy waits at `WAITING_FOR_USER` like any deployment.

| Flag | Description |
| :- | :- |
| `--yes`, `--force` | Confirm the destroy. Required. |
| `--skip-state-refresh` | Skip the state refresh during the destroy plan |
| `--checkout-updated-code` | Check out the latest revision before destroying |
| `--requires-approval` | Require approval for this destroy. `--requires-approval=false` skips it. |
| `--comment` | Destroy comment |
| `--node` | Destroy only this workflow sub-environment, by alias. The workflow environment itself stays. |
| `-f`, `--file` | Destroy every environment an [`environment apply`](#environment-apply) file lists, instead of one id |
| `--project` | With `--file`, project for entries that do not set one |
| `--wait`, `--quiet`, `--poll-interval`, `--timeout`, `--pretty` | As on `environment deploy` |

With `--file`, `destroy` takes the same file as `apply`, so a workspace comes down with the file that brought it up. Entries need no `template`.

```bash theme={null}
env0 environment destroy -f workspace.yaml --yes --wait -o compact
```

* Environments are found as `apply` finds them, and every lookup runs before anything is destroyed. `destroy -f` shares the parse-level file validation of `apply`, such as an unknown field, an entry listed twice, or a bad `ttl` or tag, and its environment lookups, so a file that fails those is rejected here too. It does not require `template`, and it does not verify workflow aliases or whether the target supports sub-environment overrides, so a file that `apply` rejects for those with exit `64` still destroys the environments it names. It still validates the variable names and types in sub-environment overrides, and an invalid type exits `64` before any request.
* Nothing to destroy is a `skip`, not an error: an environment that does not exist, one never deployed, one already destroyed, or one that already has an unfinished destroy. A rerun never queues a second destroy.
* The destroys run in parallel. A workflow environment is destroyed as a unit, with its sub-environments destroyed in reverse `needs:` order.
* env zero archives each environment once its destroy succeeds. An environment whose destroy fails or waits for approval stays live. After you fix or approve it, run the same command again to destroy what is left.
* `--requires-approval` applies to every destroy. Without it, an entry's `requiresApproval` applies, then the environment's own setting.
* `--node` and `--pretty` work only with a single environment id, and `--wait` does not stream logs.
* The output lists every entry in file order, with `name`, `projectId`, `environmentId`, `action` (`destroy` or `skip`), `deployments`, and `error`. The exit code is `1` when any environment has an `error` or a failed destroy. A destroy waiting for approval still exits `0`.

### environment archive

```bash theme={null}
env0 environment archive <environment-id>
```

Marks the environment inactive without running a deployment, so its cloud resources stay. Run `destroy` first to remove them. See [mark an environment as inactive](https://docs.envzero.com/guides/admin-guide/environments.md#mark-an-environment-as-inactive).

### environment tag list

```bash theme={null}
env0 environment tag list [<environment-id>] [--organization <organization-id>]
```

With an id, prints that environment's tags, for example `{"team": ["eng", "payments"]}`. Without one, prints every tag key and value used in the organization. `tags` is an alias for `tag`.

### environment tag set

```bash theme={null}
env0 environment tag set <environment-id> KEY=VALUE [KEY=VALUE...]
```

Replaces the whole value list of each key you name and leaves other keys alone. To keep an existing value, pass it again: `team=eng team=payments`. Prints the updated environment.

### environment tag remove

```bash theme={null}
env0 environment tag remove <environment-id> KEY [KEY...]
```

Removes each key with all of its values. Passing `KEY=VALUE` exits `64`.

Tag limits and permissions are listed in [Environment tags](https://docs.envzero.com/guides/admin-guide/environments/tags.md#limits). Tags are visible to anyone who can view the environment, so do not put secrets in them.

### environment ttl set

```bash theme={null}
env0 environment ttl set <environment-id> <duration|timestamp>
```

Replaces the environment's lifespan. A duration counts from now, not from the current end, so `ttl set <environment-id> 2h` can bring the end forward. The minimum is `1h`. Read the new end from `lifespanEndAt` in the output: env zero truncates it to the minute.

### environment ttl extend

```bash theme={null}
env0 environment ttl extend <environment-id> <duration>
```

Adds the duration to the current end, or to now if the end has passed. On an environment with no TTL, exits `2`. `extend` reads the current end and then writes a new one, so a TTL change someone makes in between is overwritten.

### environment ttl clear

```bash theme={null}
env0 environment ttl clear <environment-id>
```

Removes the TTL, so env zero never destroys the environment automatically. `lifespanEndAt` disappears from the output. For default and maximum TTLs, see [Configure policy TTL](https://docs.envzero.com/guides/policies-governance/policy-ttl.md).

### Waiting for a deployment

`--wait` polls the deployment until it reaches a final status, then exits with the [code for that status](#exit-codes). It retries `5xx` and `429` responses. A deployment that reaches `WAITING_FOR_USER` stops the wait with exit `0`. Approve it with [`deployment approve`](#deployment-approve).

While it waits, the deployment's step logs stream to stderr. Stdout still receives only the final deployment, so `--wait -o compact | jq` works. Logs keep their color on a terminal and lose it when redirected. Secrets are redacted before the logs reach the CLI.

## Deployments

### deployment list

```bash theme={null}
env0 deployment list --environment <environment-id> [--limit <n>] [--all]
```

Lists deployments, newest first. Paginated, see [Pagination](#pagination).

### deployment get

```bash theme={null}
env0 deployment get <deployment-id>
```

Includes a truncated `errorMessage`, `failedCommand`, and `exitCode` for a failed deployment.

### deployment logs

```bash theme={null}
env0 deployment logs <deployment-id> [--step <name>] [--tail <n>] [--follow] [--poll-interval <duration>]
```

Without `--follow`, returns the deployment's `status`, `failedCommand`, `exitCode`, full `errorMessage`, and `steps`, with color codes removed. Use it for the root cause of a failure:

```bash theme={null}
env0 deployment logs <deployment-id> -o compact | jq -r '.data.steps[] | select(.status == "FAIL") | .events[].message'
```

* `steps` lists every step in order, each with its `name`, `status`, and `events`: the log lines the env zero console shows for that step. A step that has not started has no events. A step with `available: false` has no events because env zero no longer stores its logs, which happens for some older deployments.
* `output` holds the deployment's Terraform or OpenTofu outputs, not the console output, so it is often `{}`.
* `--step <name>` returns only that step. Repeatable. Use a `name` from `steps`, not a `displayName`. An unknown name exits `64` and lists the valid names.
* `--tail <n>` keeps the last `n` lines of each step. A step that lost earlier lines has `truncated: true`.
* `-o table` prints the fields, then the log lines grouped by step.

With `--follow` (`-f`), streams the logs to stdout until the deployment finishes, then exits like `--wait`. `--step` and `--tail` do not combine with `--follow`. The stream is text grouped by step. With `-o json` or `-o compact` it is newline-delimited JSON, one `{"step", "level", "message", "timestamp"}` object per line, with no envelope. `--poll-interval` defaults to `3s`.

### deployment approve

```bash theme={null}
env0 deployment approve <deployment-id>
```

Resumes a deployment at `WAITING_FOR_USER`. The status changes a few seconds later. Approving a deployment that has already moved on exits `2` and is harmless. Approval rights do not approve your own deployments automatically.

### deployment cancel

```bash theme={null}
env0 deployment cancel <deployment-id>
```

Cancels a `QUEUED` deployment, or rejects one at `WAITING_FOR_USER`.

### deployment abort

```bash theme={null}
env0 deployment abort <deployment-id>
```

Aborts an `IN_PROGRESS` deployment.

`approve`, `cancel`, and `abort` return right away and take no `--wait`. To follow a resumed deployment, poll `deployment get` until its status leaves `WAITING_FOR_USER`, then run `deployment logs <deployment-id> --follow`.

## Plans

### plan list

```bash theme={null}
env0 plan list --environment <environment-id> [--limit <n>] [--all]
```

Lists plan summaries across an environment's deployments. Paginated, see [Pagination](#pagination).

### plan get

```bash theme={null}
env0 plan get <deployment-id>
```

Returns the plan stored with a deployment. Check `planAvailable` first: most deployments, including those at `WAITING_FOR_USER`, store no plan. With no plan, `resourceChanges` and `outputChanges` are `[]`, the same as a plan with no changes.

* `resourceChanges[].action` shows `delete` and `recreate` only when a plan is stored.
* The deployment's `planSummary` counts can be higher than the entries in `resourceChanges`. Use `resourceChanges` for the actual resources.
* To see what drifted, run `plan get` on the latest deployment with `type` `driftDetection`. See [Drift detection](https://docs.envzero.com/guides/admin-guide/environments/drift-detection.md).

### Cost estimation

`environment get` and `list`, `context` (under `summary` and each deployment), `plan get`, and `deployment get` and `list` include `costEstimation`: `totalMonthlyCost` and the deployment's `monthlyCostDiff`, as numbers with no currency. The `table` output assumes USD. The field is left out, which `jq` reads as `null`, when [cost estimation](https://docs.envzero.com/guides/policies-governance/cost-estimation.md) is off for the organization, the deployment produced no estimate, or the CLI could not read the estimate.

## Variables

### variable list

```bash theme={null}
env0 variable list [--environment <environment-id>] [flags]
```

Lists the variables configured on a scope, with sensitive values masked. `var` is an alias for `variable`.

| Flag | Description |
| :- | :- |
| `--environment` | Environment whose variables to list. Its organization and project are used for the other scopes. |
| `--project` | Project scope |
| `--blueprint` | Template scope |
| `--organization` | Organization scope. Required unless set by `--environment` or stored. |

## VCS connections

### vcs list

```bash theme={null}
env0 vcs list [--organization <organization-id>] [--type <type>]
```

Lists the organization's VCS connections. Pass a connection's `id` as `--vcs-connection-id` to `template create` or `environment create` for a private repository. Its `accessScope`, for example `Organization:acme`, must cover the repository's owner. `--type` filters by provider, for example `GitHub` or `GitLab`.

## Cloud inventory

`env0 inventory` compares the cloud resources that exist with the ones env zero manages, through Cloud Compass. Cloud Compass reads the CloudQuery connection stored in the organization's settings, so the CLI needs no CloudQuery credential. Every inventory command takes `--organization`, by name or id, and uses the stored organization when you omit it.

A resource is managed when Cloud Compass matched it to a resource in the state of an environment that still exists, or when it carries that environment's `env0_environment_id` tag. An unmanaged resource is one no environment manages. It may belong to other tooling, such as CloudFormation, CDK, or Terraform run outside env zero, or it may have been created by hand.

Run [`inventory status`](#inventory-status) first. Data is only as fresh as the last CloudQuery sync. When Cloud Compass cannot read its managed state, `coverage` returns `managed`, `unmanaged`, `drifted`, and `managedPct` as `null` for that group (`?` in tables), and `list` returns `managed: null`. Treat those values as unknown, not as zero.

### inventory status

```bash theme={null}
env0 inventory status [--organization <organization-name-or-id>]
```

Shows whether Cloud Compass can read CloudQuery, how many resources env zero manages, and when each integration last synced. It exits `0` either way, so check `.data.connected`. When it is `false`, `.data.reason` says why, and every other inventory command fails with that reason. When it is `true`, `.data.managedResources` counts the managed resources. A count of `0` means no environment state has been matched yet, so every resource reads as unmanaged. An absent count means the count itself failed. Each integration's `lastRun.completedAt` shows its last sync, and `lastRun.statusReason: partial_success` means some resources were not synced.

### inventory coverage

```bash theme={null}
env0 inventory coverage [flags]
```

Counts managed and unmanaged cloud resources, plus drifted resources and CloudQuery insights, grouped by account by default.

| Flag | Description |
| :- | :- |
| `--by` | Group counts by `account` (default), `type`, `cloud`, `region`, `project`, or `environment` |
| `--cloud` | Only this cloud, for example `aws`, `azure`, or `gcp` |
| `--account` | Only this cloud account, by the name Cloud Compass shows |
| `--region` | Only this region |
| `--type` | Only this resource type, by its label, for example `S3 Bucket` |
| `--search` | Only resources whose name, or one of whose tag keys or values, contains this text |
| `--organization` | Organization name or id |

`coverage` comes from a separate Cloud Compass count that includes nested records, so its totals can be higher than the `page.totalCount` of `inventory list`.

### inventory list

```bash theme={null}
env0 inventory list [flags]
```

Lists cloud resources, optionally only those env zero does or does not manage. The filters of `coverage` apply, and these flags are added:

| Flag | Description |
| :- | :- |
| `--managed` | Only resources an environment manages |
| `--unmanaged` | Only resources no environment manages |
| `--environment` | Only this environment's managed resources, each with its Terraform `address` and `module`. With `--unmanaged`, the cloud resources Cloud Compass attributes to the environment that its state does not hold. |
| `--type` | Resource type label. With `--environment`, the Terraform type instead, for example `aws_s3_bucket`. |
| `--limit` | Maximum resources to return. Default `100`. |
| `--all` | Fetch every resource instead of stopping at `--limit` |

`page.totalCount` is the full match count. The organization-wide list reads ten resources per request, so a large `--limit` or `--all` takes a while. Use `coverage` for counts. A good order is to run `coverage`, then `coverage --account <name> --by type`, then `list --unmanaged --account <name> --type <label>`.

### inventory query

```bash theme={null}
env0 inventory query <sql> [--limit <n>] [--all] [--organization <organization-name-or-id>]
```

Runs a SQL query against the CloudQuery asset warehouse, which is ClickHouse. Use it for questions the other commands do not cover. Rows come back keyed by column name.

| Flag | Description |
| :- | :- |
| `--limit` | Maximum rows to return. Default `100`. |
| `--all` | Fetch every row instead of stopping at `--limit` |
| `--organization` | Organization name or id |

These tables are the most useful:

* `cloud_assets` holds every synced resource, with `cloud`, `account`, `region`, `resource_type`, `name`, `tags`, `_cq_platform_id`, and `_cq_parent_id`.
* `resource_enrichment` rows with `enrichment_source = 'envzero'` link a resource, by `_cq_platform_id`, to the environment that manages it.
* `insight_resources` holds CloudQuery insights per resource, with `severity` and `title`.

```bash theme={null}
env0 inventory query "SELECT resource_type, uniqExact(_cq_platform_id) AS n FROM cloud_assets WHERE account = '123456789012' AND _cq_parent_id IS NULL GROUP BY resource_type ORDER BY n DESC"
```

Results are read 500 rows per request, and each request runs the query again. When a result needs more than one request and the query has no top-level `ORDER BY`, the CLI runs it as `SELECT * FROM (<query>) ORDER BY ALL` so the pages line up. A query with its own `ORDER BY` is sent as written, so end it with a unique column such as `_cq_platform_id`. Otherwise rows that tie can repeat or go missing between pages. A request that runs past the server timeout exits `75` and is not retried, so narrow the query before you run it again.

Counting `resource_enrichment` rows undercounts what env zero manages, because it holds only state matches. Take managed and unmanaged counts from `inventory status`, `coverage`, or `list`.

## Raw API requests

### api

```bash theme={null}
env0 api <path> [flags]
```

Sends one request to any env zero API endpoint and prints the response body unmodified. It is an unsupported escape hatch for endpoints no other command covers. It has no envelope, retries, pagination, or secret masking, so use a dedicated command whenever one exists. Endpoints are listed in the [API reference](https://docs.envzero.com/api-reference).

| Flag | Description |
| :- | :- |
| `-X`, `--method` | HTTP method. Default `GET`, or `POST` when `--input` is given. |
| `--input` | File to send as the request body, or `-` for stdin |
| `-H`, `--header` | Request header as `"Name: value"`. Repeatable. `Authorization` cannot be set. |
| `-i`, `--include` | Print the response status line and headers before the body |

```bash theme={null}
env0 api "/teams/organizations/$ORG"
env0 api -X DELETE "/configuration/$VARIABLE_ID"
env0 api /configuration --input variables.json
```

> [!WARNING]
>
> Responses are not masked. `GET /configuration` returns sensitive variable values in clear text, and endpoints that create a token or key return its secret. Read variables with [`variable list`](#variable-list), which masks them, and keep `api` output that may hold a secret out of logs.

* Quote the path, because `?` and `&` mean something to the shell. The path is relative to the API host and carries the query string. A full URL exits `64`, so your credentials only go to the configured host.
* The stored organization and project are not added. Put `organizationId` or `projectId` where the endpoint's reference page puts it: the path, the query, or the body.
* The body is sent as is, as `application/json` unless you pass your own `Content-Type`. It is read only from `--input`, never from a bare pipe.
* stdout gets the response body byte for byte, with no added newline. `-o` shapes only the error envelope.
* A non-2xx response is still printed to stdout, and the error envelope goes to stderr with `(HTTP <status>)` at the end of `error.message`. The exit code follows the status: `2` for other `4xx`, `3` for `401` and `403`, `4` for `404`, `75` for `429` and `5xx`, and `1` for anything unexpected, including a `3xx`, because redirects are not followed.
* A path or method the API does not have answers `403` with a message about an invalid `Authorization` header, whatever your credentials. If `organization list` works, check the method and path against the API reference.
* `api` never retries and never asks for confirmation. A write that fails with a `5xx` or exit `75` may still have taken effect, so check before you send it again.

## API keys

The CLI manages two kinds of [API key](https://docs.envzero.com/guides/admin-guide/user-role-and-team-management/api-keys.md):

| | `env0 api-key` | `env0 personal-api-key` |
| :- | :- | :- |
| Acts as | The organization | You |
| Permissions | The `--role` you grant | Yours |
| Requires organization settings permission | Yes, for `list`, `create`, and `revoke` | No |
| Keeps working after you leave | Yes | No |

Use an organization key for shared CI. Use a personal key on your own machine, or when actions must be attributed to you.

Both `create` commands print `apiKeySecret` once. No command returns it again. The returned `apiKeyId` and `apiKeySecret` are the values for `ENV0_API_KEY_ID` and `ENV0_API_KEY_SECRET`. Keys do not expire.

### api-key list

```bash theme={null}
env0 api-key list [--organization <organization-id>]
```

Lists the organization's keys without secrets, including `lastUsedAt`. Custom roles show as their id.

### api-key create

```bash theme={null}
env0 api-key create --name <name> --role <role> --yes [flags]
```

| Flag | Description |
| :- | :- |
| `--name` | Key name. Names are not unique. Required. |
| `--role` | Organization role: `Admin`, `User`, or a custom role id. Required, because env zero grants Admin when the role is omitted. |
| `--yes` | Confirm creating a long-lived credential. Required. |
| `--organization` | Organization to create the key in |
| `--project-role` | Project role as `PROJECT_ID=ROLE`, where `ROLE` is `Admin`, `Deployer`, `Planner`, `Viewer`, or a custom role id. Repeatable. |
| `--read-only` | Limit the key to read-only access |

No command shows a key's project roles after creation.

### api-key revoke

```bash theme={null}
env0 api-key revoke "<id>" --yes
```

Takes the `id` field from `api-key list`, which looks like `auth0|abc123`, not the `apiKeyId` field. Quote it, because `|` is a shell pipe. Revoking cannot be undone. The key leaves the list at once but can keep authenticating for a short time afterwards. `--force` is an alias for `--yes`.

### personal-api-key list

```bash theme={null}
env0 personal-api-key list
```

### personal-api-key create

```bash theme={null}
env0 personal-api-key create --name <name> --yes
```

Creates a key with your permissions. It stops working when your user account does.

### personal-api-key revoke

```bash theme={null}
env0 personal-api-key revoke "<id>" --yes
```

Takes the `id` field, not `apiKeyId`. Another user's key id exits `4`.

## Sign-in commands

### login

```bash theme={null}
env0 login [flags]
```

| Flag | Description |
| :- | :- |
| `--organization` | Organization, by name or id, to store instead of picking one. Defaults to `ENV0_ORGANIZATION_ID`. |
| `--project` | Project, by id, name, or `Parent / Child` path, to store instead of picking one. A name is looked up in the organization. |
| `--no-browser` | Print the sign-in URL instead of opening a browser |
| `--read-only` | With browser sign-in, request a session that cannot write |
| `--tenant` | `prod` or `dev`. Only for browser sign-in against a host outside `env0.com`. |

See [Sign-in and stored context](#sign-in-and-stored-context).

### status

```bash theme={null}
env0 status
```

Always exits `0`. Check `.data.authenticated`, not the exit code.

| Field | Description |
| :- | :- |
| `host` | API host in use |
| `authenticated` | Whether the credentials work against the host |
| `keyId` | Masked API key id |
| `credentialSource` | `oauth`, `stored`, `environment`, `flag`, or `none` |
| `credentialStorage` | `keychain` or `file` |
| `credentialFile` | Path of the credentials file, when `credentialStorage` is `file` |
| `readOnly` | `true` for a read-only session |
| `organizationId`, `organization` | Stored organization id and name |
| `projectId`, `project` | Stored project id and name |
| `telemetry`, `tracing` | `on` or `off` |
| `note` | Present when the API is unreachable or stored files cannot be read |

### logout

```bash theme={null}
env0 logout
```

Removes the current host's credentials. Keeps the stored host, organization, and project. Other hosts stay signed in.

## Agent skill

### skill install

```bash theme={null}
env0 skill install [--agent <agent>] [--global | --path <path>]
```

Writes a guide to the CLI into a coding agent's configuration.

| `--agent` | Project file |
| :- | :- |
| `claude` (default) | `.claude/skills/env0-cli/SKILL.md` |
| `cursor` | `.cursor/rules/env0-cli.mdc` |
| `codex` | `AGENTS.md` |
| `copilot` | `.github/copilot-instructions.md` |

`AGENTS.md` and the Copilot file are edited only between managed markers, so the rest of the file is kept. Running `install` again is safe. `--global` installs in your home directory, for `claude` and `codex`. `--path` writes to a file or directory you choose.

### skill show

```bash theme={null}
env0 skill show
```

Prints the guide as Markdown, not JSON. Ignores `-o`.

### skill uninstall

```bash theme={null}
env0 skill uninstall [--agent <agent>] [--global | --path <path>]
```

Deletes the Claude Code or Cursor file, or removes the managed block from a shared file.

## Other commands

| Command | Description |
| :- | :- |
| `env0 version` | Print the CLI version |
| `env0 completion <shell>` | Print a shell completion script for `bash`, `zsh`, `fish`, or `powershell` |

## Telemetry

The CLI sends anonymous usage events and request traces: the command, output format, detected agent or CI system, authentication method, error type, exit code, CLI version, and organization id. Events use a random per-install id. They never include credentials, arguments, output, resource names or ids, or file paths. Telemetry never blocks or fails a command.

To turn off both telemetry and tracing, set `ENV0_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1`. `env0 status` shows whether each is on.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.