# Replace env

URL: https://docs.hanzo.ai/docs/openapi/platform/put-platform-projects-by-project-apps-by-app-env

> Replaces an app's environment variables.

`PUT /v1/platform/projects/{project}/apps/{app}/env`

> [Platform →](https://docs.hanzo.ai/docs/openapi/platform) · [All API references →](https://docs.hanzo.ai/docs/openapi) · [Get started →](https://docs.hanzo.ai/docs/guides)

|               |                                                                      |
| ------------- | -------------------------------------------------------------------- |
| **Address**   | `https://api.hanzo.ai/v1/platform/projects/{project}/apps/{app}/env` |
| **Method**    | `PUT`                                                                |
| **Operation** | `put_platform_projects_by_project_apps_by_app_env`                   |
| **Auth**      | `Authorization: Bearer $HANZO_API_KEY`                               |

Replaces an app's environment variables.

It writes the app's whole environment set and answers the updated application.
This is the one post-create write path for env, and it REPLACES rather than
merges: a variable absent from the body is gone, and a secret dropped from the
set leaves the app's Secret on its next deploy.

Keys must match `^[A-Za-z_][A-Za-z0-9_]*$`. A value marked `secret: true` is
sealed into KMS and blanked in the database, so plaintext is never persisted —
and the write fails 503 if KMS is unavailable rather than storing one in the
clear.

The rule worth knowing: this does not restart anything. Once the app has been
deployed the secret sync is re-declared immediately so the operator
re-materialises the Secret, but RUNNING pods keep the environment they started
with until their next deploy or restart. Requires a validated principal; 401
without one.

## Request

8 fields, body `application/json` (required).

| Field          | In   | Type                   | Required | Description                                                                                                                                                              |
| -------------- | ---- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `project`      | path | string                 | yes      | Project is the project the application lives under, from the path.                                                                                                       |
| `app`          | path | string                 | yes      | App is the application's slug, from the path.                                                                                                                            |
| `app`          | body | string                 | —        | App is the application's slug, from the path.                                                                                                                            |
| `env`          | body | platform.EnvVarJSON\[] | —        | Env is the app's whole environment set, REPLACING what it had.                                                                                                           |
| `env[].key`    | body | string                 | —        | Key is the variable's name in the container, which must match `^[A-Za-z_][A-Za-z0-9_]*$`.                                                                                |
| `env[].secret` | body | boolean                | —        | Secret says the value lives in KMS and never in the database.                                                                                                            |
| `env[].value`  | body | string                 | —        | Value is the plaintext, and it is WRITE-ONLY once the entry is secret: a sealed value reads back as "", and sending "" again KEEPS what is sealed rather than wiping it. |
| `project`      | body | string                 | —        | Project is the project the application lives under, from the path.                                                                                                       |

## Response

| Status    | Body            | Meaning |
| --------- | --------------- | ------- |
| `200`     | platform.appOut | ok      |
| `default` | problem-details | refused |

`200` body — 34 fields.

| Field                 | In   | Type                   | Always | Description                                                                                                                                                                                                                          |
| --------------------- | ---- | ---------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `buildType`           | body | string                 | —      | BuildType is how a git app builds: `pack`, the zero-config default that detects the project, or `dockerfile`.                                                                                                                        |
| `createdAt`           | body | integer (int64)        | —      | CreatedAt is when the app was created, unix seconds.                                                                                                                                                                                 |
| `currentDeploymentId` | body | string                 | —      | CurrentDeploymentID is the deployment that is live — the pointer a deploy advances monotonically by version, so it never regresses to an older one.                                                                                  |
| `description`         | body | string                 | —      | Description is free text about what the app is.                                                                                                                                                                                      |
| `dockerfile`          | body | string                 | —      | Dockerfile is the path inside the repo to build from, for buildType `dockerfile`.                                                                                                                                                    |
| `domains`             | body | string\[]              | —      | Domains are the ingress hosts rendered into the app's CR, its own `&lt;slug&gt;.&lt;org&gt;.&lt;sites host&gt;` first.                                                                                                               |
| `env`                 | body | platform.EnvVarJSON\[] | —      | Env is the app's environment variables, with every SECRET value masked to "" — the plaintext is in KMS and this surface never echoes it.                                                                                             |
| `env[].key`           | body | string                 | —      | Key is the variable's name in the container, which must match `^[A-Za-z_][A-Za-z0-9_]*$`.                                                                                                                                            |
| `env[].secret`        | body | boolean                | —      | Secret says the value lives in KMS and never in the database.                                                                                                                                                                        |
| `env[].value`         | body | string                 | —      | Value is the plaintext, and it is WRITE-ONLY once the entry is secret: a sealed value reads back as "", and sending "" again KEEPS what is sealed rather than wiping it.                                                             |
| `environment`         | body | string                 | —      | Environment is the deploy target this app names, `production` when none was given.                                                                                                                                                   |
| `health`              | body | string                 | —      | Health rolls ready-vs-desired replicas up to a colour: green (all ready), yellow (some ready, or deliberately scaled to zero), red (none), or "" when the cluster reports no replica counts at all — unknown, never a guessed green. |
| `id`                  | body | string                 | —      | ID is the server-minted application id (`app_…`).                                                                                                                                                                                    |
| `image`               | body | platform.imageView     | —      |                                                                                                                                                                                                                                      |
| `image.repository`    | body | string                 | —      | Repository is the image path without a tag (ghcr.io/acme/api).                                                                                                                                                                       |
| `image.tag`           | body | string                 | —      | Tag is the tag to run: what the create declared, then RE-STAMPED on every transition to live with the tag that actually went live.                                                                                                   |
| `name`                | body | string                 | —      | Name is the display name.                                                                                                                                                                                                            |
| `namespace`           | body | string                 | —      | Namespace is where the app's cluster objects live, `tenant-&lt;org&gt;`.                                                                                                                                                             |
| `org`                 | body | string                 | —      | Org is the tenant that owns the app.                                                                                                                                                                                                 |
| `phase`               | body | string                 | —      | Phase is the operator's own `status.phase` for the app's Service CR, read from the cluster on this request.                                                                                                                          |
| `port`                | body | integer (int64)        | —      | Port is the container port traffic is sent to.                                                                                                                                                                                       |
| `projectId`           | body | string                 | —      | ProjectID is the IAM project the app lives under, and it is that project's NAME — the (org,name) key IAM identifies it by, which is also what the `:project` path segment carries.                                                   |
| `replicas`            | body | integer (int64)        | —      | Replicas is how many copies the CR declares.                                                                                                                                                                                         |
| `repo`                | body | platform.gitSource     | —      |                                                                                                                                                                                                                                      |
| `repo.branch`         | body | string                 | —      | Branch is the branch a push-to-deploy build tracks, `main` when the create named none — a push to any other branch, and every tag push, builds nothing here.                                                                         |
| `repo.provider`       | body | string                 | —      | Provider is derived from the URL — github, gitlab, bitbucket, or `git` for anything else.                                                                                                                                            |
| `repo.url`            | body | string                 | —      | URL is the clone URL a git app builds from, stored as sent once the build path's allowlist accepted it (validateRepoURL).                                                                                                            |
| `secretSync`          | body | string                 | —      | SecretSync is how far the app's secret env has got into the cluster: ""\|pending\|syncing\|ready\|failed (secrets.go).                                                                                                               |
| `secretSyncDetail`    | body | string                 | —      | SecretSyncDetail is the honest reason when the sync is not ready — a missing CRD, an RBAC grant, a per-tenant credential.                                                                                                            |
| `slug`                | body | string                 | —      | Slug is the app's identity in the cluster: the operator CR's name, the first label of its default host, and the `:app` path segment.                                                                                                 |
| `source`              | body | string                 | —      | Source is what the app deploys FROM: `git`, which builds Repo, or `image`, which runs Image as it is.                                                                                                                                |
| `status`              | body | string                 | —      | Status is the lifecycle THIS store records: draft (created, nothing in the cluster yet), building, deploying, live, stopped or error.                                                                                                |
| `storageGb`           | body | integer (int64)        | —      | StorageGB is the persistent volume size in GiB.                                                                                                                                                                                      |
| `updatedAt`           | body | integer (int64)        | —      | UpdatedAt is when it last changed, unix seconds.                                                                                                                                                                                     |

Failure carries the platform error shape — see [Errors](https://docs.hanzo.ai/docs/errors).

## Examples

**CLI**

```bash
hanzo platform projects apps env <project> <app>
```

**SDK**

**.ts**

```ts
import { Configuration, PlatformApi } from 'hanzoai';

const api = new PlatformApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.putPlatformProjectsByProjectAppsByAppEnv({ project: 'project', app: 'app', app: "<app>", env: [{"key":"<key>","secret":false,"value":"<value>"}] });
```

**.py**

```python
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import PlatformApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = PlatformApi(client).put_platform_projects_by_project_apps_by_app_env(project='project', app='app', app="<app>", env=[{"key":"<key>","secret":false,"value":"<value>"}])
```

**.go**

```go
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.PlatformAPI.PutPlatformProjectsByProjectAppsByAppEnv(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

```rust
use hanzo_client::apis::{configuration::Configuration, platform_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = platform_api::put_platform_projects_by_project_apps_by_app_env(&cfg, Default::default()).await?;
```

**.java**

```java
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.PlatformApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new PlatformApi(client).putPlatformProjectsByProjectAppsByAppEnv();
```

**HTTP**

```bash
curl -X PUT https://api.hanzo.ai/v1/platform/projects/<project>/apps/<app>/env \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "app": "<app>",
       "env": [
         {
           "key": "<key>",
           "secret": false,
           "value": "<value>"
         }
       ]
     }'
```

**MCP**

MCP reaches **platform** through the `platform` tool, which names its 38 operations with its own verbs — this one among them, under a name only MCP declares. `describe` explains any of them:

```bash
curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "get_build"
         }
       }
     }'
```

---

[Platform API](https://docs.hanzo.ai/docs/openapi/platform) · [All Hanzo APIs](https://docs.hanzo.ai/docs/openapi) · [Interactive reference](https://docs.hanzo.ai/docs/openapi/reference)
