# Application database

Endpoints for working with the SQLite database of a Galaxy application: reading the schema and rows, SQL queries, row edits, database backups and restoring from a backup. The database lives in the container's persistent `/data` volume, and the platform works with it without stopping the application.

Scope: `vibe:infra`

## Operations

- [Get database information](./database/info.md) — `GET /v1/infra/servers/:id/db`
- [Get the database schema](./database/schema.md) — `GET /v1/infra/servers/:id/db/schema`
- [Run a read-only SQL query](./database/query.md) — `POST /v1/infra/servers/:id/db/query`
- [Get table rows](./database/rows.md) — `GET /v1/infra/servers/:id/db/tables/:table/rows`
- [Add a row](./database/rows-create.md) — `POST /v1/infra/servers/:id/db/tables/:table/rows`
- [Update a row](./database/rows-update.md) — `PATCH /v1/infra/servers/:id/db/tables/:table/rows/:key`
- [Delete a row](./database/rows-delete.md) — `DELETE /v1/infra/servers/:id/db/tables/:table/rows/:key`
- [Run write SQL](./database/execute.md) — `POST /v1/infra/servers/:id/db/execute`
- [Get host preparation status](./database/prepare-status.md) — `GET /v1/infra/servers/:id/app-db/prepare`
- [Prepare the host](./database/prepare.md) — `POST /v1/infra/servers/:id/app-db/prepare`
- [List database backups](./database/backups.md) — `GET /v1/infra/servers/:id/db/backups`
- [Create a database backup](./database/snapshot.md) — `POST /v1/infra/servers/:id/db/backups`
- [Restore the database from a backup](./database/restore.md) — `POST /v1/infra/servers/:id/db/restore`

In all paths, `:id` is the ID of an application with `kind` equal to `GALAXY_APP` from [`GET /v1/infra/servers`](/docs/infra/servers/list), not the ID of the host galaxy.

Adding, updating and deleting individual rows is available only through the REST API. The `manage_app_db` MCP tool writes to the database only through SQL with `action: "execute"`.

## When the database is available

Working with the database requires four conditions:

1. **The feature is enabled for the Bitrix24 account.** The flag is the `features.appDb` field in the [`GET /v1/me`](/docs/keys-auth/me) response. With `false`, every endpoint in this section returns `403 APP_DB_DISABLED`.
2. **The application is hosted in Galaxy.** A standalone virtual machine gets `400 APP_DB_UNSUPPORTED_PLACEMENT`. For how hosting works, see [Galaxy app](/docs/infra/galaxy).
3. **A running application keeps the database in WAL mode and does not close the connection.** The platform sets the `VIBE_DB_PATH=/data/app.db` and `VIBE_DATA_DIR=/data` variables in the container unless the application author has already set them. The application opens the file from `VIBE_DB_PATH` and enables `PRAGMA journal_mode=WAL` and `PRAGMA busy_timeout=5000`. Each worker process opens one connection at startup and keeps it until the process exits, rather than opening a new one per request. If a running application has closed the connection, reading the schema and rows, SQL queries and backups return `409 APP_DB_WAL_CLOSED`. The database of a sleeping application, whose container is stopped, can be read and backed up with the connection closed: the platform opens it read-only. If the application wakes during a read or a backup, the result is discarded and the response is `409 APP_DB_BUSY`.
4. **The galaxy host is prepared.** Reads and backups need a service image on the host. Until the host is prepared, reads return `503 APP_DB_TOOL_UNAVAILABLE`. To check readiness, call [`GET /app-db/prepare`](./database/prepare-status.md). To prepare the host, call [`POST /app-db/prepare`](./database/prepare.md).

The database is accessible to the key that created the application, to the application key and to a member of the application's development team from the same Bitrix24 account. External collaborators get no access. Another user's application or a deleted one returns `404 NOT_FOUND`.

Database reads and writes, host preparation and a manual backup wake a sleeping galaxy host; the application container is not started. The host has up to 10 seconds to connect. If it does not connect in time, the response is `409 APP_DB_BUSY` with a `Retry-After` header, the wake-up continues, and retrying after the delay returns the result.

Reads see changes the application has already committed, even if the WAL journal has not yet been checkpointed into the main file. Changes from an uncommitted application transaction are not visible.

## Protective backups before writes

Before each database change, the platform saves a protective backup with `triggerType` equal to `PRE_CHANGE`. Until the backup is ready, the write call returns `409 APP_DB_BACKUP_NOT_READY` with a `Retry-After` header and must be retried. Creating a backup takes up to several minutes.

- [`POST /db/execute`](./database/execute.md) creates a new protective backup before each call.
- Adding, updating and deleting rows share one protective backup for 10 minutes. A backup for a different `file` is created no earlier than 10 minutes after the previous row-edit backup of this application. Until then, the call returns `409 APP_DB_BACKUP_NOT_READY`.

The protective backup ID is returned in the `backupId` field of the write response. You can use it to [restore the database](./database/restore.md) if the change turns out to be wrong. Row edits within 10 minutes share one backup, so restoring it undoes every row edit made since it was taken, not only the last one.

## Backups and retention

- A single SQLite file is backed up, not the application and not the whole volume. The application keeps running during the backup: the backup includes committed changes from the WAL journal.
- The daily backup runs only for a running host; the schedule does not wake the host.
- A backup is kept for seven days; the expiry is in the `expiresAt` field.
- Manual, daily and protective backups count toward the Bitrix24 account's paid storage at the size of the encrypted object. Before an application is deleted, the platform saves a backup with `triggerType` equal to `PRE_DELETE`; its storage is paid by the platform (`platformPaid: true`).
- Backups are encrypted and stored in S3-compatible object storage.
- A deletion freeze on the user account or the Bitrix24 account blocks backup creation and restore.

## Limits

| Request group | Limit per Bitrix24 account |
|---|---|
| Reads: information, schema, read-only SQL query, rows, backup list, preparation status | 60 per minute |
| Writes: write SQL, row edits, backup creation, restore, starting preparation | 20 per minute |

The table lists the platform-wide limit per Bitrix24 account, shared by all its keys and applications. Every response carries the effective limit for the key and the remaining budget in the `x-ratelimit-limit` and `x-ratelimit-remaining` headers. When the platform divides the limit across replicas, `x-ratelimit-limit` is lower than the number in the table, so pace your requests by these headers. Exceeding the limit returns `429 RATE_LIMITED` with a `Retry-After` header.

## Typical workflow

1. Check that the feature is enabled: [`GET /v1/me`](/docs/keys-auth/me) → `features.appDb: true`.
2. Check host readiness: [`GET /app-db/prepare`](./database/prepare-status.md). If `state` is not `ready`, call [`POST /app-db/prepare`](./database/prepare.md) and poll the status until `ready`.
3. View the tables: [`GET /db/schema`](./database/schema.md).
4. Read data: [`GET /db/tables/:table/rows`](./database/rows.md) or [`POST /db/query`](./database/query.md).
5. Before a large change, save a backup: [`POST /db/backups`](./database/snapshot.md) and wait for `status: "COMPLETED"` in [`GET /db/backups`](./database/backups.md).
6. Change data: [`POST /db/execute`](./database/execute.md) or individual row edits.

## See also

- [Galaxy app](/docs/infra/galaxy)
- [Full application deploy](/docs/infra/deploy/deploy)
- [Storage](/docs/storage)
- [Infrastructure](/docs/infra)
