> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# apps

> Hosted web applications — client.apps.

An app is **org-level**: it belongs to the organization, not to an agent, and outlives any session. Nineteen methods: fourteen on an app itself, plus `installs.apply` / `installs.list` / `installs.update` / `installs.context` for the declaration a project is applied from and what its crew knows, and `artifacts.list` for the catalog it is applied out of. API detail: [Apps](/docs/api/apps).

## create

```ts theme={"system"}
client.apps.create(body: AppCreate): Promise<App>
```

`POST /v1/apps`. `AppCreate` is `{ name, description?, type?, mcp? }` — `type` is `"frontend_only"` (default) or `"fullstack"`; `mcp` is the path of the MCP endpoint a `fullstack` app serves (`"/mcp"`), refused on `frontend_only`. Idempotent on the name.

## list

```ts theme={"system"}
client.apps.list(query?: ListQuery): Promise<Page<App>>
```

`GET /v1/apps`, cursor-paginated.

## get

```ts theme={"system"}
client.apps.get(id: string): Promise<App>
```

`GET /v1/apps/{id}`. A `fullstack` app's backend advances on read — poll here until `status` is terminal.

## update

```ts theme={"system"}
client.apps.update(id: string, body: AppPatch): Promise<App>
```

`PATCH /v1/apps/{id}`. `AppPatch` is `{ description?: string | null, mcp?: string | null }`. Setting `mcp` mints the app's `VETTA_MCP_TOKEN` secret and connects every agent that can access the app to its tools as `<app>.<tool>`; `null` removes both. See [Tools from your apps](/docs/capabilities/tools#tools-from-your-apps).

## delete

```ts theme={"system"}
client.apps.delete(id: string): Promise<Deleted>
```

`DELETE /v1/apps/{id}`. Tears down the hosting project and, for `fullstack`, the app database. Irreversible.

## deploy

```ts theme={"system"}
client.apps.deploy(id: string, files: Record<string, string>): Promise<AppDeployment>
```

`POST /v1/apps/{id}/deployments`. `files` maps paths to base64 contents; the build runs asynchronously.

## deployments

```ts theme={"system"}
client.apps.deployments(id: string, query?: { limit?: number }): Promise<Page<AppDeployment>>
```

`GET /v1/apps/{id}/deployments`. Non-terminal builds advance on read.

## setSecret

```ts theme={"system"}
client.apps.setSecret(id: string, body: { name: string; value: string }): Promise<AppSecret>
```

`POST /v1/apps/{id}/secrets`. Write-only: the value never comes back on any read. Overwrites an existing name.

## secrets

```ts theme={"system"}
client.apps.secrets(id: string): Promise<{ object: "list"; data: AppSecret[] }>
```

`GET /v1/apps/{id}/secrets` — names and timestamps only, never values.

## deleteSecret

```ts theme={"system"}
client.apps.deleteSecret(id: string, name: string): Promise<{ object: "app_secret"; name: string; deleted: boolean }>
```

`DELETE /v1/apps/{id}/secrets/{name}`.

## connectDomain

```ts theme={"system"}
client.apps.connectDomain(id: string, domainId: string): Promise<AppDomain>
```

`POST /v1/apps/{id}/domains`. The domain must already exist at [`client.domains`](/docs/sdk/domains); connecting is pointing, never registering.

## domains

```ts theme={"system"}
client.apps.domains(id: string): Promise<{ object: "list"; data: AppDomain[] }>
```

`GET /v1/apps/{id}/domains`.

## disconnectDomain

```ts theme={"system"}
client.apps.disconnectDomain(id: string, domain: string): Promise<{ object: "app_domain"; domain: string; deleted: boolean }>
```

`DELETE /v1/apps/{id}/domains/{domain}` — by domain name.

## query

```ts theme={"system"}
client.apps.query(id: string, query: string): Promise<{ object: "app_db_result"; rows: unknown[]; row_count: number }>
```

`POST /v1/apps/{id}/db/query`. `fullstack` apps only; a `frontend_only` app answers `feature_not_configured`. The database as a whole — tables, a REST facade over them, migrations, and this same query without naming the app — is [`client.database`](/docs/sdk/database).

## installs.apply

```ts theme={"system"}
client.apps.installs.apply(body: InstallApply): Promise<BlueprintInstall>
```

`POST /v1/blueprints/installs`. Records one applied declaration for a project — with what, by whom, and against which revision — and, when you name a published pair instead of sending one, performs the apply too.

Sending a `declaration` records only, and that is the right shape for this client: you reconciled it yourself, which is the upsert-by-name every other method here performs, so `report` comes back `null`. Omitting the declaration and naming a published `blueprint` and `template` asks the server to reconcile the catalog's bytes for you, as you, and `report` is what that run did — one line per resource, refusals included.

`expected_revision` is [`expected_version`](/docs/api/agents) copied: omit it for a deliberate unconditional apply, or send the revision you believe you are moving from and a mismatch is refused `revision_conflict`, whose message names the paths at which the stored declaration and yours disagree.

`selection: { agents: string[]; apps: string[] }` names which of the template's crew this apply provisions; omit it and the whole crew is. A deselected agent is not created, its schedules are not armed and its intake does not open; one that already exists is left running, and the report names it under `deselected` — apart from `skipped`, because a choice is not a failure. A selection that leaves out a `required` agent or app is refused `validation_failed`, naming every missing one. The install stores the `selection` it was applied with (`null` for everything, and for every install written before the field existed). See [Templates](/docs/concepts/templates).

## installs.list

```ts theme={"system"}
client.apps.installs.list(query?: { project?: string; limit?: number; after?: string }): Promise<Page<BlueprintInstall>>
```

`GET /v1/blueprints/installs`. Every install in the organization, most recently applied first; `project` narrows it to one project's.

## installs.update

```ts theme={"system"}
client.apps.installs.update(id: string, body: { answers: Record<string, unknown> }): Promise<BlueprintInstall>
```

`PATCH /v1/blueprints/installs/{id}`. Rewrites the setup answers of an applied install — the whole map, not a merge, so send every answer you mean to keep. Each key must be a question the install's artifact declares, and each value a string or a list of strings; anything else is refused `validation_failed`. The install's `context_updated_at` moves and its `revision` does not: an answer edit is not an apply, re-provisions nothing and briefs no agent again. The crew reads the new answer through [`installs.context`](#installscontext) the next time it looks.

## installs.context

```ts theme={"system"}
client.apps.installs.context(id: string): Promise<ProjectContext>
```

`GET /v1/blueprints/installs/{id}/context`. What the crew of this install knows about the business: `{ object: "project_context", project, blueprint, template, artifact_version, answers: { key, label, value }[], apps: { name, url }[], agents: { name, role }[], updated_at }`, derived from the install and its report — the answers as the artifact's questions label them, the apps with the URL each is served at, the agents with the role each was declared with. It is the same object a template agent reads through its built-in `project_context` tool.

## artifacts.list

```ts theme={"system"}
client.apps.artifacts.list(query?: {
  blueprint?: string; template?: string; limit?: number; after?: string;
}): Promise<Page<BlueprintArtifact>>
```

`GET /v1/blueprints/artifacts`. The published catalog, newest release first — every blueprint × template that can be run, carrying the setup questions to ask first, the crew and the apps it provisions, and the timers it runs.

The catalog belongs to no organization: it is the same list for everybody, and nothing a customer writes reaches it. Applying one of these rows sends its `blueprint` and `template` in place of a `declaration`, which the catalog holds server-side.
