# Apply batch

`POST https://api.instacloud.com/projects/{projectId}/apply`

**Token scope:** `account` · `org` · `project` — read-only tokens refused (not a GET).

Write a batch of user secrets, then redeploy the compute services those values reach, one deploy per service. Entries are reported written individually and services are reported deployed, started, skipped or failed individually, because the writes commit before the deploys run and a failed deploy never rolls a value back. `skipDeploy` writes without deploying. `excludeFromDeploy` drops services the caller is deploying itself; `deployOnly` names the services to deploy and ignores derivation, which is how a failed deploy is retried with no entries. A rename writes no env, so it deploys nothing on its own and needs no `deploy` grant — a batch that both renames and writes still deploys, for the write. Gated on what the body contains: `secrets.write` for any set or delete, `service.rename` for a rename, `deploy` only for what can actually deploy. A project-scoped entry is authorized against every branch, since that is where its value lands. Deploying a stopped service starts it. A suspended service is skipped instead of deployed: a deploy would invalidate the RAM snapshot suspension exists to preserve, so it is left untouched and reported with reason `suspended`.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `projectId` | path | `string` | yes |  |

## Request body (application/json)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `branch` | `string` | no | branch name (default branch if omitted) |
| `entries` | `object[]` | yes |  |
| `skipDeploy` | `boolean` | no |  |
| `excludeFromDeploy` | `string[]` | no |  |
| `deployOnly` | `string[]` | no |  |

## Response 200

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `entries` | `object[]` | yes |  |
| `entries.name` | `string` | yes |  |
| `entries.written` | `boolean` | yes |  |
| `services` | `object[]` | yes |  |
| `services.serviceId` | `string` | yes |  |
| `services.result` | `"deployed" \| "started" \| "skipped" \| "failed"` | yes |  |
| `services.reason` | `string` | no |  |

## Response 202

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | `"approval_required"` | no |  |
| `approvalId` | `string` | no |  |
| `action` | `string` | no | the gated action, or a comma-joined compound set — prefer `actions` |
| `actions` | `string[]` | no | every capability this approval covers |
| `message` | `string` | no |  |
| `url` | `string` | no | the console page where a project admin reviews this request |
| `nextActions` | `object[]` | no |  |
| `nextActions.op` | `string` | yes | Neutral logical action id, e.g. "service.add" — NOT an operationId or a CLI/MCP tool name; each client maps it to its own surface. |
| `nextActions.reason` | `string` | yes | Natural-language, human/LLM-facing "why do this now". |
| `nextActions.args` | `object` | no | Suggested, flat named arguments; "<placeholder>"s where a value is unknown. |
| `nextActions.gated` | `boolean` | no | True if the action passes a governance gate. |

## Response 400

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 403

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 404

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 409

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 429

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |
