# Environments API

> Named, versioned sets of environment variables and secret files that VMYard applies to machines.

- [List environments](#list-environments): `GET /v1/environments`
- [Create an environment](#create-an-environment): `POST /v1/environments`
- [Get an environment](#get-an-environment): `GET /v1/environments/{environment_id}`
- [Change an environment](#change-an-environment): `PATCH /v1/environments/{environment_id}`
- [Delete an environment](#delete-an-environment): `DELETE /v1/environments/{environment_id}`
- [Set a variable](#set-a-variable): `PUT /v1/environments/{environment_id}/vars/{key}`
- [Remove a variable](#remove-a-variable): `DELETE /v1/environments/{environment_id}/vars/{key}`
- [Set a secret file](#set-a-secret-file): `PUT /v1/environments/{environment_id}/files`
- [Remove a secret file](#remove-a-secret-file): `DELETE /v1/environments/{environment_id}/files`
- [List versions](#list-versions): `GET /v1/environments/{environment_id}/versions`
- [Reveal values](#reveal-values): `POST /v1/environments/{environment_id}/reveal`
- [Upgrade machines](#upgrade-machines): `POST /v1/environments/{environment_id}/upgrade`

## List environments

`GET /v1/environments`

- **API key:** `Authorization: Bearer vmy_…` with preset `read_only`, `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie.
- **Who:** Any member.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `X-Organization-ID` | header | `string (uuid)` | No | Organization context. Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | — | [`EnvironmentList`](/docs/api/schemas#environmentlist) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl "$VMYARD_API_URL/v1/environments" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

```json title="Response 200"
{
  "items": [
    {
      "created_at": "string",
      "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "current_version": {
        "change": {},
        "created_at": "string",
        "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "files": [
          {
            "path": "notes/todo.txt",
            "size": 0
          }
        ],
        "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "vars": [
          "string"
        ],
        "version": 0
      },
      "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "is_default": false,
      "name": "staging",
      "safe_for_third_parties": false,
      "updated_at": "string"
    }
  ]
}
```

## Create an environment

`POST /v1/environments`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `X-Organization-ID` | header | `string (uuid)` | No | Organization context. Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`CreateEnvironmentRequest`](/docs/api/schemas#createenvironmentrequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `files` | [`SecretFileInput[]`](/docs/api/schemas#secretfileinput) | No |  |
| `is_default` | `boolean \| null` | No | Defaults to `true` when the organization has no default environment. |
| `name` | `string \| null` | No | Lowercase DNS label, unique in the organization. Defaults to `base`. |
| `safe_for_third_parties` | `boolean` | No |  |
| `vars` | `map of string` | No | Variable names (`^[A-Za-z_][A-Za-z0-9_]{0,127}$`) to values. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `201` | The created environment (not the list) | [`EnvironmentDto`](/docs/api/schemas#environmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | A role other than owner/admin, or an API key without full_access | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | `environment_name_taken` or `environment_limit_reached` | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `503` | `environments_unavailable`: no encryption keys configured | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/environments" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "staging",
  "vars": {
    "DATABASE_URL": "postgres://app@db.internal/app",
    "LOG_LEVEL": "info"
  },
  "files": [
    {
      "path": "secrets/service-account.json",
      "contents": "{\"type\": \"service_account\"}"
    }
  ]
}'
```

```json title="Response 201"
{
  "created_at": "string",
  "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "current_version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  },
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "is_default": false,
  "name": "staging",
  "safe_for_third_parties": false,
  "updated_at": "string"
}
```

## Get an environment

`GET /v1/environments/{environment_id}`

- **API key:** `Authorization: Bearer vmy_…` with preset `read_only`, `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie.
- **Who:** Any member.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | — | [`EnvironmentDto`](/docs/api/schemas#environmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

```json title="Response 200"
{
  "created_at": "string",
  "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "current_version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  },
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "is_default": false,
  "name": "staging",
  "safe_for_third_parties": false,
  "updated_at": "string"
}
```

## Change an environment

`PATCH /v1/environments/{environment_id}`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`UpdateEnvironmentRequest`](/docs/api/schemas#updateenvironmentrequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `is_default` | `boolean \| null` | No | `true` makes this the default; `false` leaves the organization without one. |
| `name` | `string \| null` | No |  |
| `safe_for_third_parties` | `boolean \| null` | No |  |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | Metadata only; no new version | [`EnvironmentDto`](/docs/api/schemas#environmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | Conflicts with the current state, such as a reused idempotency key with another body (`idempotency_conflict`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X PATCH "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "is_default": true
}'
```

```json title="Response 200"
{
  "created_at": "string",
  "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "current_version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  },
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "is_default": false,
  "name": "staging",
  "safe_for_third_parties": false,
  "updated_at": "string"
}
```

## Delete an environment

`DELETE /v1/environments/{environment_id}`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `204` | Deleted; machines keep the versions they have | — |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X DELETE "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

## Set a variable

`PUT /v1/environments/{environment_id}/vars/{key}`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `key` | path | `string` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`SetVariableRequest`](/docs/api/schemas#setvariablerequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `value` | `string` | Yes | At most 32 KiB without NUL. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | A new version was written | [`EnvironmentDto`](/docs/api/schemas#environmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `503` | Not available on this server. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X PUT "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID/vars/$KEY" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "value": "info"
}'
```

```json title="Response 200"
{
  "created_at": "string",
  "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "current_version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  },
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "is_default": false,
  "name": "staging",
  "safe_for_third_parties": false,
  "updated_at": "string"
}
```

## Remove a variable

`DELETE /v1/environments/{environment_id}/vars/{key}`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `key` | path | `string` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | A new version was written | [`EnvironmentDto`](/docs/api/schemas#environmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Unknown environment or `variable_not_found` | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `503` | Not available on this server. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X DELETE "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID/vars/$KEY" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

```json title="Response 200"
{
  "created_at": "string",
  "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "current_version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  },
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "is_default": false,
  "name": "staging",
  "safe_for_third_parties": false,
  "updated_at": "string"
}
```

## Set a secret file

`PUT /v1/environments/{environment_id}/files`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`SecretFileInput`](/docs/api/schemas#secretfileinput))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `contents` | `string` | Yes |  |
| `path` | `string` | Yes | Relative to `/work`: no leading `/`, `~`, `.`, `..`, or empty components, and not under `.vmyard`. |
| `encoding` | [`FileEncodingDto`](/docs/api/schemas#fileencodingdto) | No |  |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | A new version was written | [`EnvironmentDto`](/docs/api/schemas#environmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `503` | Not available on this server. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X PUT "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID/files" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "path": "secrets/token.txt",
  "contents": "s3cr3t\n"
}'
```

```json title="Response 200"
{
  "created_at": "string",
  "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "current_version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  },
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "is_default": false,
  "name": "staging",
  "safe_for_third_parties": false,
  "updated_at": "string"
}
```

## Remove a secret file

`DELETE /v1/environments/{environment_id}/files`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`RemoveFileRequest`](/docs/api/schemas#removefilerequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | `string` | Yes |  |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | A new version was written | [`EnvironmentDto`](/docs/api/schemas#environmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Unknown environment or `file_not_found` | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `503` | Not available on this server. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X DELETE "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID/files" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "path": "secrets/token.txt"
}'
```

```json title="Response 200"
{
  "created_at": "string",
  "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "current_version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  },
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "is_default": false,
  "name": "staging",
  "safe_for_third_parties": false,
  "updated_at": "string"
}
```

## List versions

`GET /v1/environments/{environment_id}/versions`

- **API key:** `Authorization: Bearer vmy_…` with preset `read_only`, `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie.
- **Who:** Any member.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | — | [`EnvironmentVersionList`](/docs/api/schemas#environmentversionlist) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID/versions" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

```json title="Response 200"
{
  "items": [
    {
      "change": {},
      "created_at": "string",
      "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "files": [
        {
          "path": "notes/todo.txt",
          "size": 0
        }
      ],
      "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "vars": [
        "string"
      ],
      "version": 0
    }
  ]
}
```

## Reveal values

`POST /v1/environments/{environment_id}/reveal`

- **API key:** `Authorization: Bearer vmy_…` with preset `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner or Admin.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`RevealRequest`](/docs/api/schemas#revealrequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `version_id` | `string (uuid) \| null` | No | A version of this environment; omitted reveals the current version. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | Values and file contents; audited as `environment.revealed` | [`RevealedEnvironmentDto`](/docs/api/schemas#revealedenvironmentdto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | A role other than owner/admin, or an API key without full_access | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `503` | Not available on this server. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID/reveal" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json title="Response 200"
{
  "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "files": [
    {
      "contents": "string",
      "encoding": "text",
      "path": "notes/todo.txt"
    }
  ],
  "vars": {},
  "version": {
    "change": {},
    "created_at": "string",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "files": [
      {
        "path": "notes/todo.txt",
        "size": 0
      }
    ],
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "vars": [
      "string"
    ],
    "version": 0
  }
}
```

## Upgrade machines

`POST /v1/environments/{environment_id}/upgrade`

- **API key:** `Authorization: Bearer vmy_…` with preset `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `environment_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`UpgradeEnvironmentRequest`](/docs/api/schemas#upgradeenvironmentrequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `machine_ids` | `string (uuid)[] \| null` | No | Machines to upgrade; omitted upgrades every running machine created with this environment. At most 100. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | One apply operation per upgraded machine; repeating the request replays them | [`UpgradeEnvironmentResponse`](/docs/api/schemas#upgradeenvironmentresponse) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | `environment_upgrade_conflict`: a machine changed meanwhile; retry | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | A machine is not one created with this environment | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/environments/$ENVIRONMENT_ID/upgrade" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "machine_ids": [
    "0b6f6c55-2f1d-4f0e-9a51-6f0f4cba7a10"
  ]
}'
```

```json title="Response 202"
{
  "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operations": [
    {
      "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "operation_url": "/v1/operations/6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f"
    }
  ],
  "skipped": [
    {
      "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "reason": "not_running"
    }
  ],
  "version": 0,
  "version_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f"
}
```
