# Variables

> Plain environment variables and references.

Plain environment variables and references. Values are visible to members who can read the project.

| Operation | Method and path |
| --- | --- |
| [List plain variables](#list-plain-variables) | `GET /v1/projects/{project}/environments/{environment}/variables` |
| [Set plain variables (create or replace)](#set-plain-variables-create-or-replace) | `POST /v1/projects/{project}/environments/{environment}/variables` |
| [Unset a plain variable](#unset-a-plain-variable) | `DELETE /v1/projects/{project}/environments/{environment}/variables/{name}` |

## List plain variables

```http
GET /v1/projects/{project}/environments/{environment}/variables
```

- **Operation:** `listVariables`
- **Scopes:** `variables:read`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `service` | query | string | Only this service's variables (plus shared ones when include_shared=true). |
| `include_shared` | query | boolean |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of Variable objects. | [VariableList](/docs/api/objects/variable-list/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/variables \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "name": "DATABASE_URL",
      "value": "${{ databases.main.pooled_url }}",
      "kind": "reference",
      "scope": "service",
      "service_id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
      "public": false,
      "build": false,
      "managed_by_manifest": true,
      "updated_by": {
        "type": "user",
        "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
        "display": "Stewart Webb"
      },
      "updated_at": "2026-10-23T10:00:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Set plain variables (create or replace)

```http
POST /v1/projects/{project}/environments/{environment}/variables
```

Values that look like secrets (high-entropy keys, known token patterns) are refused with a fix suggesting `guv secrets set`.

- **Operation:** `setVariables`
- **Scopes:** `variables:write`
- **CLI:** `guv variables set`
- **Approval:** In environments with protection turned on (usually production), changes by agents and by roles outside `approver_roles` return `approval_required`.
- **Idempotency-Key:** required; supports `?dry_run=true`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |

**Request body** (`application/json`, required): [VariableSetRequest](/docs/api/objects/variable-set-request/)

| Field | Type | Description |
| --- | --- | --- |
| `service` | string | Service name or id. Omit for shared variables. |
| `variables` * | array of [VariableInput](/docs/api/objects/variable-input/) |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. With `dry_run=true`: a Plan (application/vnd.guv.plan+json) and nothing changes. | [VariableSetResult](/docs/api/objects/variable-set-result/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/projects/shopfront/environments/production/variables \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "service": "web",
  "variables": [
    {
      "name": "NODE_ENV",
      "value": "production"
    },
    {
      "name": "NEXT_PUBLIC_POSTHOG_KEY",
      "value": "phc_xxx",
      "public": true
    }
  ]
}'
```

**Example response**

```json
{
  "variables": [
    {
      "name": "DATABASE_URL",
      "value": "${{ databases.main.pooled_url }}",
      "kind": "reference",
      "scope": "service",
      "service_id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
      "public": false,
      "build": false,
      "managed_by_manifest": true,
      "updated_by": {
        "type": "user",
        "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
        "display": "Stewart Webb"
      },
      "updated_at": "2026-10-23T10:00:00Z"
    }
  ],
  "redeploy_required": [
    {
      "type": "service",
      "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
      "name": "web",
      "url": "/v1/projects/primecarers/services/web"
    }
  ],
  "fix": "guv deploy --service web --env production"
}
```

## Unset a plain variable

```http
DELETE /v1/projects/{project}/environments/{environment}/variables/{name}
```

- **Operation:** `unsetVariable`
- **Scopes:** `variables:write`
- **Approval:** In environments with protection turned on (usually production), changes by agents and by roles outside `approver_roles` return `approval_required`.
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `name` * | path | string | Variable name. |
| `service` | query | string | Service the variable belongs to; omit for shared. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `204` No Content | Done. No body. | none |

**Errors:** `400`, `401`, `403`, `404`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X DELETE https://api.guv.dev/v1/projects/shopfront/environments/production/variables/stripe-secret-key \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```
