## Prepare the host

`POST /v1/infra/servers/:id/app-db/prepare`

Starts preparing the galaxy host for working with the application database: installs missing packages and the service image for reading the database. Preparation runs in the background. Track its progress with the host preparation status.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|----------|
| `id` (path) | string (UUID) | yes | ID of an application with `kind` equal to `GALAXY_APP`. List: [`GET /v1/infra/servers`](/docs/infra/servers/list) |

The request body may be empty or `{}`, no larger than 4096 bytes. Any body field and any query string parameter are rejected with `400 VALIDATION_ERROR`.

## Examples

### curl — personal key

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/APP_ID/app-db/prepare" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### curl — OAuth application

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/APP_ID/app-db/prepare" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### JavaScript — personal key

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${appId}/app-db/prepare`,
  {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
    body: '{}',
  }
)
const { success, data, error } = await res.json()
if (!success) throw new Error(error.code)
// 202 — preparation is running, poll GET at the same address. 200 — the host is already ready
console.log(res.status, data.state)
```

### JavaScript — OAuth application

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${appId}/app-db/prepare`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
      'Content-Type': 'application/json',
    },
    body: '{}',
  }
)
const { success, data, error } = await res.json()
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | `true` if preparation was started, is already running or is not needed |
| `data.state` | string | `preparing` — preparation was started by this call or was already running, HTTP `202`. `ready` — the host is already ready, HTTP `200` |
| `data.steps` | array | Seven steps in a fixed order: `disk`, `python3`, `zstd`, `age`, `curl`, `image`, `verify` |
| `data.steps[].id` | string | Step. The values are listed in the [preparation status](/docs/infra/database/prepare-status) |
| `data.steps[].state` | string | `pending`, `running`, `done`, `present` or `failed`. In the start response, components already on the host are marked `present`, the remaining steps `pending` |
| `data.missing` | string[] | Components missing on the host: `python3`, `zstd`, `age`, `curl`, `image`. With `ready`, the array is empty |
| `data.freeBytes` | integer | The smaller of the free space on the root partition and on the Docker images partition, in bytes. Returned when preparation was already running and a measurement was taken |

## Response example

Preparation started, HTTP `202`:

```json
{
  "success": true,
  "data": {
    "state": "preparing",
    "missing": ["image"],
    "steps": [
      { "id": "disk", "state": "pending" },
      { "id": "python3", "state": "present" },
      { "id": "zstd", "state": "present" },
      { "id": "age", "state": "present" },
      { "id": "curl", "state": "present" },
      { "id": "image", "state": "pending" },
      { "id": "verify", "state": "pending" }
    ]
  }
}
```

The host is already ready, HTTP `200`:

```json
{
  "success": true,
  "data": {
    "state": "ready",
    "steps": [
      { "id": "disk", "state": "done" },
      { "id": "python3", "state": "present" },
      { "id": "zstd", "state": "present" },
      { "id": "age", "state": "present" },
      { "id": "curl", "state": "present" },
      { "id": "image", "state": "present" },
      { "id": "verify", "state": "done" }
    ],
    "missing": []
  }
}
```

## Error response example

400 — a field was passed in the body:

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid preparation parameters"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `VALIDATION_ERROR` | The body contains a field, or a query string parameter was passed |
| 400 | `APP_DB_UNSUPPORTED_PLACEMENT` | The application is not hosted in Galaxy |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 401 | `INVALID_API_KEY` | The key is not recognized |
| 402 | `BILLING_FROZEN` | The portal's billing is frozen |
| 403 | `INFRA_SCOPE_REQUIRED` | The key lacks the `vibe:infra` scope |
| 403 | `APP_DB_DISABLED` | Application database access is not enabled for the portal |
| 403 | `APP_DB_READONLY_KEY` | The key is read-only |
| 403 | `SERVER_WAKE_BLOCKED` | The galaxy host is asleep and waking it is blocked |
| 404 | `NOT_FOUND` | The application or its host was not found, was deleted or is not accessible to this key |
| 409 | `APP_DB_BUSY` | The host did not wake up within 10 seconds or is busy with another operation. Retry after the pause from `Retry-After` |
| 413 | `PAYLOAD_TOO_LARGE` | The body exceeds 4096 bytes |
| 429 | `RATE_LIMITED` | The write request limit for the portal was exceeded — [Limits](/docs/infra/database#limits). The pause is in the `Retry-After` header |
| 503 | `APP_DB_TOOL_UNAVAILABLE` | The service channel to the host is unavailable, or the host returned an invalid state. Retry after the pause from `Retry-After` |
| 503 | `user_self_deletion_pending` | The key owner is deleting their account on the portal |

If the preparation itself fails, the cause is not returned as an error of this call. It comes as the `failed` state with `data.errorCode` in the [preparation status](/docs/infra/database/prepare-status).

Full list of common API errors — [Errors](/docs/errors).

## Known specifics

- **What is installed on the host.** Only the missing `python3`, `zstd`, `age`, `curl` and the pinned service image for reading the database. Packages are installed, with signature verification, from the Ubuntu repositories configured on the host. The installation plan is checked before each package: upgrading or removing already installed packages is not allowed. Packages do not start or restart services, the host is not rebooted, and the application and Docker are not restarted. At least 6 GiB of free space is required on the root partition and on the Docker images partition. Preparation is limited to 15 minutes.
- **The platform performs the preparation.** Commands reach the host through the platform's service channel. Neither you nor the application needs access to the host for this.
- **Repeated call.** While preparation is running, the call starts nothing and returns the current state with HTTP `202`. After `failed`, the call starts preparation again; components already installed are skipped. If the start response was lost, check the [preparation status](/docs/infra/database/prepare-status) first rather than retrying the start automatically.
- **Preparation also starts without this call.** A new galaxy host starts it on its own at first boot. Creating a database backup — manual, daily or protective — also starts preparation on a host that is not ready, unless the previous preparation ended with `LOW_DISK`, `DPKG_BROKEN`, `APT_PLAN_UNSAFE` or `UNSUPPORTED_HOST`. This call starts it even after such codes.
- **Duration.** On a test host where the packages were already installed and only the service image was downloaded, preparation took less than 10 seconds.

## See also

- [Get host preparation status](/docs/infra/database/prepare-status)
- [Get database information](/docs/infra/database/info)
- [Create a database backup](/docs/infra/database/snapshot)
- [Application database](/docs/infra/database)
