# Environment variables and secrets

Each value belongs to a project, and either to one environment or to all of them (`environment: null`). A value set for an environment overrides the project-wide one with the same name.

Changes take effect on the next deploy; every write says so with `"appliesOn": "next_deploy"`.

## Environment variables

| Request | Who | What it does |
| --- | --- | --- |
| `GET /v1/projects/{projectId}/env` | Owners, admins, developers, tokens with `env:read` | Lists variables with their values. `?environment=production` returns what that environment sees, overrides applied. |
| `PUT /v1/projects/{projectId}/env/{key}` | Owners, admins, developers, tokens with `env:write` | `{ "value", "environment"? }`. Creates or replaces. |
| `DELETE /v1/projects/{projectId}/env/{key}` | Same as `PUT` | `{ "environment"? }`, or no body for the project-wide value. Removes it, or `404` if there's nothing to remove. |

```json
{ "variables": [{ "key": "PUBLIC_SITE_NAME", "value": "Cookbook", "environment": null, "updatedAt": "2026-10-27T12:00:00.000Z" }] }
```

Without `?environment=`, the list has one entry per value, sorted by key, so a key set both project-wide and for production appears twice.

## Secrets

No route ever returns a secret's value. Values are encrypted with the project's own key before they're stored, and are decrypted only to hand them to your app at deploy.

| Request | Who | What it does |
| --- | --- | --- |
| `GET /v1/projects/{projectId}/secrets` | Owners, admins, developers, tokens with `env:read` | Lists secret names, never values. Same `?environment=` as above. |
| `PUT /v1/projects/{projectId}/secrets/{key}` | Owners, admins, developers, tokens with `env:write` | `{ "value", "environment"? }`. Creates or replaces. |
| `DELETE /v1/projects/{projectId}/secrets/{key}` | Same as `PUT` | `{ "environment"? }`. Removes it. |

```json
{ "secrets": [{ "key": "STRIPE_SECRET_KEY", "environment": null, "updatedAt": "2026-10-23T12:00:00.000Z" }] }
```

## Rules for both

- Names: upper-case letters, digits and underscores, starting with a letter, at most 64 characters. Names starting `FS_` are reserved. Anything else is `400 validation_failed`.
- Values: at most 5 KB.
- An environment that doesn't exist in the project is `404 not_found`.
- Every `PUT` and `DELETE` is in the audit log (`env_var.set`, `env_var.deleted`, `secret.set`, `secret.deleted`) with the name and environment, never the value.
- Viewers can't read either list.
- A deploy token reaches only its own project. One made for a single environment can read only that environment (`?environment=` is required).