## Создать копию базы

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

Ставит в очередь ручную копию SQLite-файла приложения. Копия создаётся в фоновом режиме, приложение во время копирования продолжает работать.

## Параметры

| Параметр | Тип | Обяз. | Описание |
|----------|-----|:-----:|----------|
| `id` (path) | string (UUID) | да | ID приложения с `kind` равным `GALAXY_APP`. Список: [`GET /v1/infra/servers`](/docs/infra/servers/list) |

## Поля запроса (body)

| Поле | Тип | Обяз. | Описание |
|------|-----|:-----:|----------|
| `file` | string | нет | Путь к файлу базы относительно тома `/data`, до 1024 байт в UTF-8. Без поля копируется файл из переменной `VIBE_DB_PATH` контейнера приложения, а если она не задана — `app.db`. Абсолютный путь, сегменты `.` и `..`, пустые сегменты и управляющие символы отклоняются. Список файлов базы: [`GET /db`](/docs/infra/database/info) |

Тело необязательно: без тела и с пустым объектом `{}` копируется файл по умолчанию. С заголовком `Content-Type: application/json` тело не может быть пустым — передайте `{}`. Других полей тело не принимает, размер тела — до 4 КиБ.

## Примеры

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

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

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

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

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

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${appId}/db/backups`,
  {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
    body: JSON.stringify({}),
  }
)
const { success, data, error } = await res.json()
if (!success) throw new Error(`${error.code}: ${error.message}`)
console.log('Копия в очереди:', data.backupId)
```

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

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

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

HTTP-статус успешного ответа — `202 Accepted`: копия принята в очередь, но ещё не создана.

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | `true`, когда копия поставлена в очередь |
| `data.backupId` | string | ID копии. Под этим `id` копия появляется в [`GET /db/backups`](/docs/infra/database/backups) |
| `data.status` | string | Всегда `QUEUED` |

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

```json
{
  "success": true,
  "data": {
    "backupId": "9e4d2c1b-6a3f-4b8e-a7d5-0c1f2e3d4b5a",
    "status": "QUEUED"
  }
}
```

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

409 — повторный запрос сразу после создания копии, предыдущая копия этого приложения ещё не завершена:

```json
{
  "success": false,
  "error": {
    "code": "APP_DB_BUSY",
    "message": "The database is busy with a live write, retry shortly",
    "details": {
      "source": "sqlite",
      "retryable": true,
      "retryAfterSeconds": 1
    }
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `VALIDATION_ERROR` | Тело содержит неизвестное поле или `file` нарушает правила пути |
| 400 | `FST_ERR_CTP_EMPTY_JSON_BODY` | Передан заголовок `Content-Type: application/json` без тела. Передайте `{}` или уберите заголовок |
| 400 | `APP_DB_INVALID_FILE` | Поле `file` не передано, а значение `VIBE_DB_PATH` контейнера не указывает на допустимый путь внутри `/data` |
| 400 | `APP_DB_UNSUPPORTED_PLACEMENT` | Приложение размещено не в Галактике |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |
| 401 | `INVALID_API_KEY` | Ключ не опознан |
| 402 | `BILLING_FROZEN` | Баланс портала заморожен, копия не поставлена в очередь |
| 402 | `BILLING_INSUFFICIENT` | Баланс портала исчерпан, хранилище приостановлено. Копия не поставлена в очередь |
| 403 | `INFRA_SCOPE_REQUIRED` | У ключа нет скоупа `vibe:infra` |
| 403 | `APP_DB_READONLY_KEY` | Ключ выпущен только для чтения |
| 403 | `APP_DB_DISABLED` | Работа с базой приложения не включена для портала |
| 403 | `SERVER_WAKE_BLOCKED` | Хост спит, и для него запрещено пробуждение |
| 404 | `NOT_FOUND` | Приложения с таким `id` нет на портале, оно удалено или у ключа нет к нему доступа |
| 404 | `APP_DB_NOT_FOUND` | У приложения нет тома с данными |
| 409 | `APP_DB_BUSY` | Приложение занято: предыдущая копия ещё в статусе `QUEUED` или `RUNNING`, идёт восстановление базы, деплой или переезд приложения. С `details.source` равным `host` — на хосте восстанавливается база другого приложения, хост занят другой операцией или не вышел на связь. Повторите через `details.retryAfterSeconds` секунд |
| 413 | `PAYLOAD_TOO_LARGE` | Тело больше 4 КиБ |
| 429 | `RATE_LIMITED` | Превышен лимит запросов на запись для портала — [Лимиты](/docs/infra/database#лимиты). Пауза — в заголовке `Retry-After` |
| 503 | `APP_DB_TOOL_UNAVAILABLE` | Хост не подготовлен к работе с базой или платформа временно не может к нему обратиться. Готовность хоста — [`GET /app-db/prepare`](/docs/infra/database/prepare-status) |
| 503 | `user_self_deletion_pending` | Владелец ключа запросил удаление своего аккаунта на портале. Доступ вернётся после отмены удаления |

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

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

**Готовность копии.** Ответ `202` не означает, что копия создана. Опрашивайте [`GET /db/backups`](/docs/infra/database/backups) и ищите элемент с `id`, равным `backupId`: копия готова при `status` равном `COMPLETED`, а при `FAILED` причину называет `errorCode`. На тестовом приложении с копией размером 937 байт копия дошла от `QUEUED` до `COMPLETED` за 1 мин 40 с.

**Одна копия за раз.** Пока предыдущая копия приложения не завершена, повторы запроса через `details.retryAfterSeconds` получают тот же `409 APP_DB_BUSY`. Текст `message` причину не называет: при `details.source` равном `sqlite` он говорит о записи в базу, даже когда базу занимает незавершённая копия. Вместо частых повторов дождитесь завершения копии по списку копий.

Где хранятся копии, сколько они живут и как оплачиваются — [Копии и их хранение](/docs/infra/database#копии-и-их-хранение).

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

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