## Получить список копий базы

`GET /v1/infra/servers/:id/db/backups`

Возвращает копии SQLite-базы приложения от новых к старым и состояние последнего восстановления базы. Список читается из истории платформы, поэтому запрос не будит хост и не обращается к контейнеру.

## Параметры

| Параметр | Тип | Обяз. | По умолч. | Описание |
|----------|-----|:-----:|-----------|----------|
| `id` (path) | string (UUID) | да | — | ID приложения с `kind` равным `GALAXY_APP`. Список: [`GET /v1/infra/servers`](/docs/infra/servers/list) |
| `limit` (query) | number | нет | `50` | Число копий на странице, от `1` до `100` |
| `cursor` (query) | string | нет | — | Значение `data.nextCursor` из предыдущего ответа, без изменений. До 1024 символов: латинские буквы, цифры, `-` и `_` |
| `sourceAppId` (query) | string | нет | значение `id` | ID другого приложения того же портала, копии которого нужно показать, в том числе удалённого. Список: [`GET /v1/infra/servers`](/docs/infra/servers/list), ID удалённого приложения — поле `sourceAppId` его копий. Если у ключа нет доступа к данным этого приложения или такого приложения нет, `data.items` приходит пустым, а не ошибкой |

**Пагинация.** Копии упорядочены по `createdAt`, при равенстве — по `id`, оба по убыванию. Номера страницы нет: следующую страницу запрашивайте с `cursor`, равным `data.nextCursor`. Значение `null` в `data.nextCursor` означает конец списка. Курсор действует только для того же приложения и того же `sourceAppId`. Общего числа копий ответ не содержит.

## Примеры

### curl — личный ключ

```bash
curl "https://vibecode.bitrix24.tech/v1/infra/servers/APP_ID/db/backups?limit=3" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth-приложение

```bash
curl "https://vibecode.bitrix24.tech/v1/infra/servers/APP_ID/db/backups?limit=3" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — личный ключ

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${appId}/db/backups?limit=3`,
  { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)
const { success, data, error } = await res.json()
if (!success) throw new Error(`${error.code}: ${error.message}`)

for (const copy of data.items) {
  console.log(copy.id, copy.triggerType, copy.status, copy.expiresAt)
}
if (data.restore) {
  console.log('Восстановление:', data.restore.status, data.restore.phase)
}
// Следующая страница: data.nextCursor в параметр cursor без изменений
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${appId}/db/backups?limit=3`,
  {
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)
const { success, data, error } = await res.json()
if (!success) throw new Error(`${error.code}: ${error.message}`)
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | `true` при успехе |
| `data.items` | array | Копии базы на текущей странице |
| `data.items[].id` | string | ID копии. Передаётся в `backupId` при [восстановлении](/docs/infra/database/restore) |
| `data.items[].sourceAppId` | string (UUID) | ID приложения, с базы которого снята копия |
| `data.items[].platformPaid` | boolean | `true` — хранение копии оплачивает платформа, `false` — хранилище портала |
| `data.items[].captureScope` | string | Что скопировано. Для копий базы всегда `SQLITE` |
| `data.items[].status` | string | `QUEUED` — копия в очереди, `RUNNING` — создаётся, `COMPLETED` — готова, `FAILED` — не удалась, `EXPIRED` — срок хранения истёк, `FLAGGED` — копирование остановлено и ждёт службы поддержки, `DELETED` — удалена |
| `data.items[].triggerType` | string | `MANUAL` — ручная копия, `SCHEDULED` — ежедневная, `PRE_CHANGE` — защитная перед изменением базы, `PRE_DELETE` — перед удалением приложения |
| `data.items[].file` | string \| null | Путь скопированного файла относительно тома `/data` |
| `data.items[].sizeBytes` | string \| null | Размер копии в байтах, десятичным числом в строке. `null`, пока копия не завершена |
| `data.items[].createdAt` | string | Время постановки копии в очередь, ISO 8601 в UTC |
| `data.items[].completedAt` | string \| null | Время завершения копии, ISO 8601 в UTC. `null`, пока копия не завершена |
| `data.items[].expiresAt` | string \| null | Время окончания хранения, ISO 8601 в UTC. `null`, пока копия не завершена |
| `data.items[].errorCode` | string \| null | Причина неудачи у копии в статусе `FAILED`: `APP_DB_BUSY`, `APP_DB_WAL_CLOSED`, `APP_DB_BACKUP_TIMEOUT`, `APP_DB_CORRUPT`, `APP_DB_TOOL_UNAVAILABLE`, `APP_DB_BACKUP_INVALID`. У причин без публичного кода — `null` |
| `data.nextCursor` | string \| null | Курсор следующей страницы. `null` — страниц больше нет |
| `data.restore` | object \| null | Восстановление базы этого приложения: идущее, а если такого нет — последнее. `null`, если базу приложения не восстанавливали. Не зависит от `sourceAppId` |
| `data.restore.restoreId` | string | ID восстановления из ответа [`POST /db/restore`](/docs/infra/database/restore) |
| `data.restore.backupId` | string | ID копии, из которой восстанавливается база |
| `data.restore.targetAppId` | string (UUID) | ID приложения, базу которого восстанавливают |
| `data.restore.sourceAppId` | string (UUID) | ID приложения, с базы которого снята копия |
| `data.restore.status` | string | `QUEUED` — в очереди, `RUNNING` — выполняется, `COMPLETED` — база заменена, `FAILED` — восстановление не удалось |
| `data.restore.phase` | string \| null | Шаг восстановления. Рабочий порядок: `VALIDATE`, `PREPARE`, `PRE_CHANGE`, `STAGE`, `STOPPING`, `SWAPPING`, `VERIFY`, `STARTING`, `CLEANUP`. `CLEANUP` остаётся и после завершения, при `COMPLETED` и при `FAILED`. `ROLLBACK` — возврат исходного файла после сбоя, `RECOVERY_REQUIRED` — восстановление остановлено и ждёт службы поддержки |
| `data.restore.errorCode` | string \| null | Причина неудачи, например `APP_DB_RESTORE_FAILED`. `null`, пока сбоя нет |
| `data.restore.backupCreatedAt` | string | Время создания копии-источника, ISO 8601 в UTC |
| `data.restore.createdAt` | string | Время приёма запроса на восстановление, ISO 8601 в UTC |
| `data.restore.completedAt` | string \| null | Время завершения, ISO 8601 в UTC. `null`, пока восстановление идёт |

## Пример ответа

Первая страница из трёх копий (`?limit=3`), восстановлений не было:

```json
{
  "success": true,
  "data": {
    "restore": null,
    "items": [
      {
        "id": "9e4d2c1b-6a3f-4b8e-a7d5-0c1f2e3d4b5a",
        "sourceAppId": "5c0e8f2a-3d1b-4a7e-9f60-2b8c4d1e7a93",
        "platformPaid": false,
        "captureScope": "SQLITE",
        "status": "COMPLETED",
        "triggerType": "MANUAL",
        "file": "app.db",
        "sizeBytes": "937",
        "createdAt": "2026-10-20T08:26:17.942Z",
        "completedAt": "2026-10-20T08:28:00.046Z",
        "expiresAt": "2026-10-27T08:28:00.046Z",
        "errorCode": null
      },
      {
        "id": "1f7a3b9c-2e4d-4c6a-8b0e-5d9f7a1c3e2b",
        "sourceAppId": "5c0e8f2a-3d1b-4a7e-9f60-2b8c4d1e7a93",
        "platformPaid": false,
        "captureScope": "SQLITE",
        "status": "COMPLETED",
        "triggerType": "PRE_CHANGE",
        "file": "app.db",
        "sizeBytes": "937",
        "createdAt": "2026-10-20T08:23:15.565Z",
        "completedAt": "2026-10-20T08:25:00.061Z",
        "expiresAt": "2026-10-27T08:25:00.061Z",
        "errorCode": null
      },
      {
        "id": "4b8c6d2e-9f1a-4e3b-b5c7-7a2d0e6f8c19",
        "sourceAppId": "5c0e8f2a-3d1b-4a7e-9f60-2b8c4d1e7a93",
        "platformPaid": false,
        "captureScope": "SQLITE",
        "status": "COMPLETED",
        "triggerType": "PRE_CHANGE",
        "file": "app.db",
        "sizeBytes": "937",
        "createdAt": "2026-10-20T08:21:47.863Z",
        "completedAt": "2026-10-20T08:23:00.087Z",
        "expiresAt": "2026-10-27T08:23:00.087Z",
        "errorCode": null
      }
    ],
    "nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI2In0"
  }
}
```

После завершения восстановления (`?limit=2`). Первая в списке — защитная копия, которую платформа сохранила перед заменой файла:

```json
{
  "success": true,
  "data": {
    "restore": {
      "restoreId": "cm2r7x4k10003qv8n5d2b6w1z",
      "backupId": "9e4d2c1b-6a3f-4b8e-a7d5-0c1f2e3d4b5a",
      "targetAppId": "5c0e8f2a-3d1b-4a7e-9f60-2b8c4d1e7a93",
      "sourceAppId": "5c0e8f2a-3d1b-4a7e-9f60-2b8c4d1e7a93",
      "status": "COMPLETED",
      "phase": "CLEANUP",
      "errorCode": null,
      "backupCreatedAt": "2026-10-20T08:26:17.942Z",
      "createdAt": "2026-10-20T08:28:12.755Z",
      "completedAt": "2026-10-20T08:40:30.029Z"
    },
    "items": [
      {
        "id": "7d3e1a5b-8c2f-4d9a-9e6b-3f0a4c7d2e81",
        "sourceAppId": "5c0e8f2a-3d1b-4a7e-9f60-2b8c4d1e7a93",
        "platformPaid": false,
        "captureScope": "SQLITE",
        "status": "COMPLETED",
        "triggerType": "PRE_CHANGE",
        "file": "app.db",
        "sizeBytes": "937",
        "createdAt": "2026-10-20T08:30:30.278Z",
        "completedAt": "2026-10-20T08:32:00.080Z",
        "expiresAt": "2026-10-27T08:32:00.080Z",
        "errorCode": null
      },
      {
        "id": "9e4d2c1b-6a3f-4b8e-a7d5-0c1f2e3d4b5a",
        "sourceAppId": "5c0e8f2a-3d1b-4a7e-9f60-2b8c4d1e7a93",
        "platformPaid": false,
        "captureScope": "SQLITE",
        "status": "COMPLETED",
        "triggerType": "MANUAL",
        "file": "app.db",
        "sizeBytes": "937",
        "createdAt": "2026-10-20T08:26:17.942Z",
        "completedAt": "2026-10-20T08:28:00.046Z",
        "expiresAt": "2026-10-27T08:28:00.046Z",
        "errorCode": null
      }
    ],
    "nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI2In1"
  }
}
```

## Пример ответа при ошибке

400 — курсор изменён или выдан для другого приложения-источника:

```json
{
  "success": false,
  "error": {
    "code": "APP_DB_INVALID_FILE",
    "message": "Invalid database file inside /data"
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `APP_DB_INVALID_FILE` | Значение `cursor` не читается как курсор этого списка: изменено вручную или выдано для другого `sourceAppId`. Текст `message` говорит о файле, но в этом методе код относится к курсору. Начните обход заново без `cursor` |
| 400 | `VALIDATION_ERROR` | Неизвестный параметр запроса, `limit` вне диапазона `1`–`100`, недопустимые символы в `cursor` или `sourceAppId` |
| 400 | `APP_DB_UNSUPPORTED_PLACEMENT` | Приложение размещено не в Галактике |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |
| 401 | `INVALID_API_KEY` | Ключ не опознан |
| 403 | `INFRA_SCOPE_REQUIRED` | У ключа нет скоупа `vibe:infra` |
| 403 | `APP_DB_DISABLED` | Работа с базой приложения не включена для портала |
| 404 | `NOT_FOUND` | Приложения с таким `id` нет на портале, оно удалено или у ключа нет к нему доступа |
| 429 | `RATE_LIMITED` | Превышен лимит запросов на чтение для портала — [Лимиты](/docs/infra/database#лимиты). Пауза — в заголовке `Retry-After` |
| 503 | `APP_DB_TOOL_UNAVAILABLE` | Платформа временно не может обслужить базу этого приложения. Повторите запрос позже |
| 503 | `user_self_deletion_pending` | Владелец ключа запросил удаление своего аккаунта на портале. Доступ вернётся после отмены удаления |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Известные особенности

**Ожидание копии и восстановления.** Завершение ручной копии и восстановления проверяйте повторным вызовом этого списка: копию — по `status` элемента с нужным `id`, восстановление — по `data.restore` с нужным `restoreId`. Сколько это занимает — [Создать копию базы](/docs/infra/database/snapshot) и [Восстановить базу из копии](/docs/infra/database/restore). Каждый опрос расходует лимит запросов на чтение, общий для всех чтений базы портала.

**Остановка, которая ждёт поддержки.** Копия в статусе `FLAGGED` и восстановление с фазой `RECOVERY_REQUIRED` сами не завершатся: восстановление в этом случае остаётся в статусе `RUNNING`. Повторные опросы ответ не изменят — обратитесь в службу поддержки и назовите `id` копии или `restoreId`.

## Смотрите также

- [Создать копию базы](/docs/infra/database/snapshot)
- [Восстановить базу из копии](/docs/infra/database/restore)
- [Получить сведения о базе](/docs/infra/database/info)
- [Копии и их хранение](/docs/infra/database#копии-и-их-хранение)
- [База данных приложения](/docs/infra/database)
