# Create apps — POST /v1/platform/apps

URL: https://docs.hanzo.ai/docs/openapi/platform/post-platform-apps

> Builds a git repository into an image and writes the declaration that names it — a values file in `hanzoai/universe` under `charts/app/values/<namespace>/<name>.yaml`, which the `fleet` ApplicationSet renders as one Application.

`POST /v1/platform/apps`

> [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/apps` |
| **Method**    | `POST`                                  |
| **Operation** | `post_platform_apps`                    |
| **Auth**      | `Authorization: Bearer $HANZO_API_KEY`  |

Builds a git repository into an image and writes the declaration that names it — a values file in `hanzoai/universe` under `charts/app/values/<namespace>/<name>.yaml`, which the `fleet` ApplicationSet renders as one Application. That file IS the deployment: nothing else has to be applied.

`repo` is a GitHub repository — `owner/name` or `https://github.com/owner/name` — or another https clone URL. A GitHub repository must be one the caller may see: their own connected GitHub, or the installations their org holds for an org admin (the same set `GET /v1/provider/github/repos` lists); anything else is 404. It is built at the name GitHub gives it now, from `ref`, which defaults to its default branch. Any other URL is an org admin's to build.

`partOf` names the project the app belongs to, and defaults to the app's own name.

Every member of the org may deploy; WHERE it lands is the difference. An org admin commits to universe main and CD applies it — in one call, naming the tag its own build will push, which CD pulls once the build lands. Anyone else opens a review: the declaration is pushed to `deploy/<namespace>/<name>/<tag>`, `declaration.review` is the link that opens the pull request, and nothing deploys until it is merged. `mode` may say `branch` to make an admin's deploy a review too; a non-admin naming `commit` is refused, never downgraded.

A member's deploy is bounded by who asked: one build at a time and never the org's last build slot, which is kept for its admins; ten deploys an hour; five open review branches. Past a bound it answers 429. A review branch is deleted once main declares its tag, or after seven days unmerged; `notice` says so, and that the public `env` values the branch carries are cleartext in universe, where deleting the branch does not unpublish them.

A release of an image an earlier call built (`tag` given) is proven pullable before main is pointed at it — a declaration naming an image the registry cannot serve is an ImagePullBackOff with no rollback path.

Omit `tag` to build; give it to declare an image an earlier call already built, which is how a green build is released without rebuilding it. That release is an org admin's: a member's deploy always builds, and carries public `env` only — a sealed value would write the running app's secrets before the review is merged, so a member naming one is refused.

An org is its name: the values DIRECTORY, the destination NAMESPACE and the AppProject FENCE are all `<org>`, and the image is `<registry>/<org>/<app>`. None of them is a request field — the directory decides what CD admits the sync under and the repository decides what the cluster pulls, so a caller who could name either could reach outside its own org.

`org` is an ACT-AS, not a placement field: it defaults to the caller's own, and naming another requires SuperAdmin. So does naming a RESERVED org — the platform's own namespace family (the brands and their environments, the control and delivery planes, `admin`) — even when it is the caller's own, because an IAM org named `kube-system` does not own Kubernetes. Both refuse rather than downgrade, so an escape attempt is never indistinguishable from a normal request.

A host outside the caller's org subtree is refused: claim and verify a custom domain first.

## Request

16 fields, body `application/json`.

| Field          | In   | Type          | Required | Description |
| -------------- | ---- | ------------- | -------- | ----------- |
| `build`        | body | boolean       | —        |             |
| `dockerfile`   | body | string        | —        |             |
| `env`          | body | declareEnv\[] | —        |             |
| `env[].name`   | body | string        | —        |             |
| `env[].public` | body | boolean       | —        |             |
| `env[].value`  | body | string        | —        |             |
| `host`         | body | string        | —        |             |
| `mode`         | body | string        | —        |             |
| `name`         | body | string        | —        |             |
| `org`          | body | string        | —        |             |
| `partOf`       | body | string        | —        |             |
| `port`         | body | integer       | —        |             |
| `ref`          | body | string        | —        |             |
| `replicas`     | body | integer       | —        |             |
| `repo`         | body | string        | —        |             |
| `tag`          | body | string        | —        |             |

## Response

| Status | Body        | Meaning |
| ------ | ----------- | ------- |
| `2XX`  | declareResp | Success |

`2XX` body — 62 fields.

| Field                                       | In   | Type          | Always | Description |
| ------------------------------------------- | ---- | ------------- | ------ | ----------- |
| `app`                                       | body | Declaration   | —      |             |
| `app.application`                           | body | string        | —      |             |
| `app.automated`                             | body | boolean       | —      |             |
| `app.component`                             | body | string        | —      |             |
| `app.digest`                                | body | string        | —      |             |
| `app.env`                                   | body | declareEnv\[] | —      |             |
| `app.env[].name`                            | body | string        | —      |             |
| `app.env[].public`                          | body | boolean       | —      |             |
| `app.env[].value`                           | body | string        | —      |             |
| `app.hosts`                                 | body | string\[]     | —      |             |
| `app.name`                                  | body | string        | —      |             |
| `app.org`                                   | body | string        | —      |             |
| `app.partOf`                                | body | string        | —      |             |
| `app.path`                                  | body | string        | —      |             |
| `app.project`                               | body | string        | —      |             |
| `app.replicas`                              | body | integer       | —      |             |
| `app.repository`                            | body | string        | —      |             |
| `app.secrets`                               | body | SecretRef\[]  | —      |             |
| `app.secrets[].env`                         | body | string        | —      |             |
| `app.secrets[].keys`                        | body | string\[]     | —      |             |
| `app.secrets[].path`                        | body | string        | —      |             |
| `app.secrets[].project`                     | body | string        | —      |             |
| `app.tag`                                   | body | string        | —      |             |
| `build`                                     | body | buildRef      | —      |             |
| `build.id`                                  | body | string        | —      |             |
| `build.image`                               | body | string        | —      |             |
| `build.job`                                 | body | string        | —      |             |
| `build.ref`                                 | body | string        | —      |             |
| `build.repo`                                | body | string        | —      |             |
| `build.status`                              | body | string        | —      |             |
| `declaration`                               | body | declareResult | —      |             |
| `declaration.changed`                       | body | boolean       | —      |             |
| `declaration.commit`                        | body | string        | —      |             |
| `declaration.created`                       | body | boolean       | —      |             |
| `declaration.declaration`                   | body | Declaration   | —      |             |
| `declaration.declaration.application`       | body | string        | —      |             |
| `declaration.declaration.automated`         | body | boolean       | —      |             |
| `declaration.declaration.component`         | body | string        | —      |             |
| `declaration.declaration.digest`            | body | string        | —      |             |
| `declaration.declaration.env`               | body | declareEnv\[] | —      |             |
| `declaration.declaration.env[].name`        | body | string        | —      |             |
| `declaration.declaration.env[].public`      | body | boolean       | —      |             |
| `declaration.declaration.env[].value`       | body | string        | —      |             |
| `declaration.declaration.hosts`             | body | string\[]     | —      |             |
| `declaration.declaration.name`              | body | string        | —      |             |
| `declaration.declaration.org`               | body | string        | —      |             |
| `declaration.declaration.partOf`            | body | string        | —      |             |
| `declaration.declaration.path`              | body | string        | —      |             |
| `declaration.declaration.project`           | body | string        | —      |             |
| `declaration.declaration.replicas`          | body | integer       | —      |             |
| `declaration.declaration.repository`        | body | string        | —      |             |
| `declaration.declaration.secrets`           | body | SecretRef\[]  | —      |             |
| `declaration.declaration.secrets[].env`     | body | string        | —      |             |
| `declaration.declaration.secrets[].keys`    | body | string\[]     | —      |             |
| `declaration.declaration.secrets[].path`    | body | string        | —      |             |
| `declaration.declaration.secrets[].project` | body | string        | —      |             |
| `declaration.declaration.tag`               | body | string        | —      |             |
| `declaration.live`                          | body | boolean       | —      |             |
| `declaration.mode`                          | body | string        | —      |             |
| `declaration.ref`                           | body | string        | —      |             |
| `declaration.review`                        | body | string        | —      |             |
| `notice`                                    | body | string        | —      |             |

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

## Examples

**CLI**

```bash
hanzo platform apps create
```

**SDK**

**.ts**

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

const api = new PlatformApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postPlatformApps({ build: false, dockerfile: "<dockerfile>" });
```

**.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).post_platform_apps(build=False, dockerfile="<dockerfile>")
```

**.go**

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

resp, _, err := client.PlatformAPI.PostPlatformApps(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::post_platform_apps(&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).postPlatformApps();
```

**HTTP**

```bash
curl -X POST https://api.hanzo.ai/v1/platform/apps \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "build": false,
       "dockerfile": "<dockerfile>"
     }'
```

**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)
