
## Delete object

`DELETE /v1/storage/objects/:key`

Marks the object as deleted (soft delete). The object's `deletedAt` field is set to the deletion date. The physical bytes remain in storage for up to 30 days and are then removed automatically.

DELETE itself does not restore or release the object: GET, HEAD, the old anonymous URL of a `PUBLIC` object, and a repeated DELETE return `410 STORAGE_OBJECT_DELETED` throughout the retention period. The old bytes cannot be restored through the API. A later [direct upload (Path A)](/docs/storage/upload/direct) may write **new** bytes into the same row when the object has `COMPLETED` status and has no multipart session. Multipart upload (Path C) can also reuse an eligible personal-key `COMPLETED` object after this explicit DELETE. Both paths preserve its `id`, `createdAt`, `key`, physical address, owner tuple, and `visibility`, and clear `deletedAt` after a successful upload. Path B remains disabled with 503; ineligible deleted states return 409.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|---------|
| `key` (path) | string | yes | Logical object key, URL-encoded. For keys with `/` (e.g. `users/42/avatar.png`) pass it as `users%2F42%2Favatar.png`. List of keys: [`GET /v1/storage/objects`](/docs/storage/objects/list) |

## Examples

### curl — personal key

```bash
curl -s -X DELETE "https://vibecode.bitrix24.com/v1/storage/objects/docs-test%2Fverify.txt" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl -s -X DELETE "https://vibecode.bitrix24.com/v1/storage/objects/docs-test%2Fverify.txt" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const key = encodeURIComponent('docs-test/verify.txt')
const res = await fetch(`https://vibecode.bitrix24.com/v1/storage/objects/${key}`, {
  method: 'DELETE',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
  },
})
const body = await res.json()
if (body.object?.deletedAt) {
  console.log('Object deleted:', body.object.deletedAt)
}
```

### JavaScript — OAuth application

```javascript
const key = encodeURIComponent('docs-test/verify.txt')
const res = await fetch(`https://vibecode.bitrix24.com/v1/storage/objects/${key}`, {
  method: 'DELETE',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})
const body = await res.json()
if (body.object?.deletedAt) {
  console.log('Object deleted:', body.object.deletedAt)
}
```

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `object.id` | string (CUID) | Internal object identifier. List: [`GET /v1/storage/objects`](/docs/storage/objects/list) |
| `object.key` | string | Logical key. List: [`GET /v1/storage/objects`](/docs/storage/objects/list) |
| `object.contentType` | string | Content MIME type |
| `object.sizeBytes` | string | Size in bytes (a string due to JSON limits on large numbers) |
| `object.sha256` | string \| null | SHA-256 hash of the content, if it was computed at upload |
| `object.visibility` | string | `PRIVATE` or `PUBLIC` |
| `object.uploadStatus` | string | Upload status (`COMPLETED`) |
| `object.createdAt` | string (ISO 8601) | Creation date |
| `object.deletedAt` | string (ISO 8601) | Deletion date — set after a successful deletion |
| `object.contentUpdatedAt` | string (ISO 8601) | When the content last became current. A soft delete does **not** move it |

## Response example

```json
{
  "object": {
    "id": "cmv6mzshp1cpn4bqqik5gchff",
    "key": "docs-test/verify.txt",
    "contentType": "text/plain",
    "sizeBytes": "40",
    "sha256": "a7056068db07cebc7b4d646c1ae83f45b4bda578c57b76b582f289bfaa8f8a34",
    "visibility": "PRIVATE",
    "uploadStatus": "COMPLETED",
    "createdAt": "2026-10-13T12:08:01.021Z",
    "deletedAt": "2026-10-13T12:08:01.377Z",
    "contentUpdatedAt": "2026-10-13T12:08:01.020Z"
  }
}
```

## Error response example

410 — object already deleted (repeated call):

```json
{
  "success": false,
  "error": {
    "code": "STORAGE_OBJECT_DELETED",
    "message": "Object docs-test/verify.txt was deleted"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `STORAGE_INVALID_KEY` | The key contains invalid characters, starts with `/` or `.`, contains `..`, or exceeds 1024 characters |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 403 | `STORAGE_SCOPE_REQUIRED` | The API key has no `vibe:storage` scope |
| 404 | `STORAGE_OBJECT_NOT_FOUND` | No object with this key exists, or it belongs to another owner |
| 409 | `STORAGE_MULTIPART_IN_PROGRESS` | The object is in an unfinished multipart-upload state — first call [`/multipart/abort`](/docs/storage/upload/multipart-abort) |
| 410 | `STORAGE_OBJECT_DELETED` | The object was already deleted earlier |
| 503 | `STORAGE_STS_UNAVAILABLE` | The storage credentials service is temporarily unavailable — retry the request |
| 503 | `STORAGE_FEATURE_DISABLED` | Storage is temporarily unavailable on the platform |

The full list of general API errors — [Errors](/docs/errors).

## Known specifics

- **Keys with `/` in the path.** A slash in the key is not a route separator — pass it URL-encoded. In JavaScript use `encodeURIComponent(key)`.
- **A deleted object is immediately unavailable.** Until an eligible Path A upload or, for a personal key after explicit DELETE, a Path C upload writes new bytes, GET and HEAD by key return `410 STORAGE_OBJECT_DELETED` for the full 30-day period before physical purge. The object-list request does not include deleted objects. Reusing the row replaces the content with new bytes; it does not restore the old content.
- **Do not delete source-code snapshots with this method.** Objects under the `source/` prefix belong to servers and applications, and their logical key is the same across owners — every one of them has `source/current.tar.gz`. The request matches one of the available rows by key, so an arbitrary one would be deleted. Versions are removed through source-code operations — [Source code storage](/docs/source-storage).

- **An app key with no employee session sees only the app-shared file.** Employees of an app claim
  logical names independently of each other, so several objects may hold one name. A request made with
  an app key and NO `Authorization` header addresses the app-shared file; if the name is held solely by
  per-employee files, the response is `404 STORAGE_OBJECT_NOT_FOUND`. To reach an employee's file, pass
  that employee's session in `Authorization: Bearer`, or address the object by its identifier.

## See also

- [Download object](/docs/storage/objects/get)
- [List objects](/docs/storage/objects/list)
- [Abort multipart upload](/docs/storage/upload/multipart-abort)
- [Public access GET](/docs/storage/objects/public-get)
