## Delete a row

`DELETE /v1/infra/servers/:id/db/tables/:table/rows/:key`

Deletes one row from a table in a Galaxy application's SQLite database by its opaque key. A deleted row can be brought back only by restoring the whole database from a protective backup.

Before the write, the platform saves a protective backup of the database — see [Protective backups before writes](/docs/infra/database#protective-backups-before-writes). Access conditions — [When the database is available](/docs/infra/database#when-the-database-is-available).

## Parameters

The request has no body: a request with a body is rejected with `400 VALIDATION_ERROR`.

| Parameter | Type | Required | Description |
|----------|-----|:-----:|---------|
| `id` (path) | string | yes | ID of an application with `kind` equal to `GALAXY_APP`. List: [`GET /v1/infra/servers`](/docs/infra/servers/list) |
| `table` (path) | string | yes | Exact table name from `data.tables[].name` in the [`GET /db/schema`](/docs/infra/database/schema) response, up to 256 bytes in UTF-8. The table must have `editability.editable: true`: views, virtual tables and tables with neither a primary key nor an available `rowid` cannot be edited |
| `key` (path) | string | yes | Row key — the `data.rowKeys[i]` item of the [`GET /db/tables/:table/rows`](/docs/infra/database/rows) response, aligned with the `data.rows[i]` row. A base64url string up to 1024 characters, inserted into the path as is. `null` in `rowKeys` means the row cannot be addressed: the key column value is `null`, a BLOB or too long. The key is built from the primary key or, if there is none, from `rowid`, unless a column with that name shadows it. Do not modify the key or write it to logs: it contains the values of the row's key columns |
| `file` (query) | string | no | Relative path to the database file inside the application volume, up to 1024 bytes in UTF-8. Defaults to the `VIBE_DB_PATH` value of the application container, or `app.db` if it is not set. Absolute paths, parent-directory traversal and symbolic links are not allowed. Must match the file the `key` was read from |

## Examples

### curl — personal key

```bash
curl -X DELETE "https://vibecode.bitrix24.com/v1/infra/servers/7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33/db/tables/customers/rows/eyJ2IjoxLCJhcHBJZCI6IjdjMmIxZjA4LTNhNGQtNGU5MS05YjZjLTJmNWU4YTFkMGMzMyIsImZpbGUiOiJhcHAuZGIiLCJ0YWJsZSI6ImN1c3RvbWVycyIsImtpbmQiOiJwayIsImNvbHVtbnMiOlsiaWQiXSwidmFsdWVzIjpbeyJ0eXBlIjoiaW50ZWdlciIsInZhbHVlIjoiNCJ9XX0" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl -X DELETE "https://vibecode.bitrix24.com/v1/infra/servers/7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33/db/tables/customers/rows/eyJ2IjoxLCJhcHBJZCI6IjdjMmIxZjA4LTNhNGQtNGU5MS05YjZjLTJmNWU4YTFkMGMzMyIsImZpbGUiOiJhcHAuZGIiLCJ0YWJsZSI6ImN1c3RvbWVycyIsImtpbmQiOiJwayIsImNvbHVtbnMiOlsiaWQiXSwidmFsdWVzIjpbeyJ0eXBlIjoiaW50ZWdlciIsInZhbHVlIjoiNCJ9XX0" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
// The key comes from data.rowKeys of the GET /db/tables/customers/rows response
const rowKey = 'eyJ2IjoxLCJhcHBJZCI6IjdjMmIxZjA4LTNhNGQtNGU5MS05YjZjLTJmNWU4YTFkMGMzMyIsImZpbGUiOiJhcHAuZGIiLCJ0YWJsZSI6ImN1c3RvbWVycyIsImtpbmQiOiJwayIsImNvbHVtbnMiOlsiaWQiXSwidmFsdWVzIjpbeyJ0eXBlIjoiaW50ZWdlciIsInZhbHVlIjoiNCJ9XX0'

const res = await fetch(`https://vibecode.bitrix24.com/v1/infra/servers/7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33/db/tables/customers/rows/${rowKey}`, {
  method: 'DELETE',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
  },
})

const { success, data, error } = await res.json()
if (res.status === 409 && error.code === 'APP_DB_BACKUP_NOT_READY') {
  // The protective backup is being prepared — retry after Retry-After seconds
  console.log('Retry in', res.headers.get('Retry-After'), 's')
} else {
  console.log('Rows deleted:', data.rowCount)
}
```

### JavaScript — OAuth application

```javascript
// The key comes from data.rowKeys of the GET /db/tables/customers/rows response
const rowKey = 'eyJ2IjoxLCJhcHBJZCI6IjdjMmIxZjA4LTNhNGQtNGU5MS05YjZjLTJmNWU4YTFkMGMzMyIsImZpbGUiOiJhcHAuZGIiLCJ0YWJsZSI6ImN1c3RvbWVycyIsImtpbmQiOiJwayIsImNvbHVtbnMiOlsiaWQiXSwidmFsdWVzIjpbeyJ0eXBlIjoiaW50ZWdlciIsInZhbHVlIjoiNCJ9XX0'

const res = await fetch(`https://vibecode.bitrix24.com/v1/infra/servers/7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33/db/tables/customers/rows/${rowKey}`, {
  method: 'DELETE',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const { success, data, error } = await res.json()
if (res.status === 409 && error.code === 'APP_DB_BACKUP_NOT_READY') {
  // The protective backup is being prepared — retry after Retry-After seconds
  console.log('Retry in', res.headers.get('Retry-After'), 's')
} else {
  console.log('Rows deleted:', data.rowCount)
}
```

## Response fields

A successful deletion returns `200 OK` with a body, not `204 No Content`.

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data.file` | string | Database file the row was deleted from |
| `data.table` | string | Table name |
| `data.rowCount` | number | Number of rows deleted, always `1` |
| `data.durationMs` | number | Write execution time in the database, in milliseconds |
| `data.backupId` | string | ID of the protective backup taken before the first row edit of the last 10 minutes. Row edits in this window share one backup, and restoring it undoes all of them. Backup list — [`GET /db/backups`](/docs/infra/database/backups), restore — [`POST /db/restore`](/docs/infra/database/restore) |

## Response example

```json
{
  "success": true,
  "data": {
    "file": "app.db",
    "rowCount": 1,
    "durationMs": 1.2,
    "table": "customers",
    "backupId": "cb2fcdfd-bace-4932-a276-31d737337f3d"
  }
}
```

## Error response example

404 — the row with this key no longer exists, for example after a repeated deletion:

```json
{
  "success": false,
  "error": {
    "code": "APP_DB_ROW_NOT_FOUND",
    "message": "Database row not found"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 404 | `APP_DB_ROW_NOT_FOUND` | No row was found by the key: the row was already deleted or its key value changed |
| 409 | `APP_DB_ROW_NOT_EDITABLE` | The row cannot be deleted: the key does not match this application, file or table, the key matches more than one row, or the table is not editable |
| 409 | `APP_DB_BACKUP_NOT_READY` | The protective backup is being prepared; the row was not deleted. `error.details` contains `backupId`, `retryable: true` and `retryAfterSeconds`; the `Retry-After` header carries the same number of seconds, from 1 to 60. Retry the same request after the pause |
| 409 | `APP_DB_BUSY` | A deploy, migration or restore of the application or another database write is in progress, or waking the application or host is blocked. The `Retry-After` header is present |
| 409 | `APP_DB_WAL_CLOSED` | The protective backup was not created: a running application has closed its database connection in WAL mode. The row was not deleted; there is no `Retry-After` header. Configure the application to keep the connection open |
| 400 | `VALIDATION_ERROR` | The request failed validation: it has a body, a query string parameter other than `file`, an invalid `file`, a table name longer than 256 bytes, or a key longer than 1024 characters or with characters outside base64url |
| 400 | `APP_DB_INVALID_FILE` | The `file` path does not point to a valid database file inside `/data` |
| 400 | `APP_DB_UNSUPPORTED_PLACEMENT` | The application is not hosted in Galaxy |
| 404 | `APP_DB_TABLE_NOT_FOUND` | The database has no `:table` table |
| 404 | `APP_DB_NOT_FOUND` | There is no volume or database file: the application has not created a database yet |
| 404 | `NOT_FOUND` | The application was not found, was deleted or is not accessible to this key |
| 413 | `PAYLOAD_TOO_LARGE` | The full input of the service process exceeds 96 KiB |
| 422 | `APP_DB_SQL_ERROR` | The database rejected the deletion; the transaction was rolled back. The cause is in `error.details.reason`, for example `constraint_failed` |
| 422 | `APP_DB_QUERY_TIMEOUT` | The write did not finish within 30 seconds; the transaction was rolled back |
| 422 | `APP_DB_CORRUPT` | The database file is corrupted |
| 422 | `APP_DB_BACKUP_TIMEOUT` | The protective backup did not finish within the allotted time; the row was not deleted |
| 422 | `APP_DB_BACKUP_INVALID` | The protective backup failed validation; the row was not deleted |
| 503 | `APP_DB_PRE_CHANGE_FAILED` | The protective backup was not created; the row was not deleted. The error is reported once — the next call starts a new backup |
| 503 | `APP_DB_WRITE_OUTCOME_UNKNOWN` | The database response was lost; the row may have been deleted. `retryable: false` — do not retry automatically |
| 503 | `APP_DB_TOOL_UNAVAILABLE` | The host is not prepared or is temporarily unavailable. The `Retry-After` header is present |
| 402 | `BILLING_FROZEN` | The portal's billing is frozen |
| 402 | `BILLING_INSUFFICIENT` | The portal balance is insufficient to create a protective backup |
| 403 | `APP_DB_READONLY_KEY` | A read-only key cannot change the database |
| 403 | `APP_DB_DISABLED` | Database access is not enabled for the portal |
| 403 | `INFRA_SCOPE_REQUIRED` | The key lacks the `vibe:infra` scope |
| 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 |
| 401 | `MISSING_API_KEY` | `X-Api-Key` is missing |

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

## Known specifics

**There is no row version check.** The deletion does not verify whether the row changed after it was read: the row is deleted by key even if its values have changed.

## See also

- [Add a row](/docs/infra/database/rows-create)
- [Update a row](/docs/infra/database/rows-update)
- [Get table rows](/docs/infra/database/rows)
- [Get the database schema](/docs/infra/database/schema)
- [Restore the database from a backup](/docs/infra/database/restore)
- [List database backups](/docs/infra/database/backups)
- [Application database](/docs/infra/database)
