# Keep earlier versions of objects

> Turn on versioning to keep what an overwrite or a delete replaces, then list, restore or delete versions and remove delete markers.

Source: https://www.coritan.com/docs/object-storage/versioning/

In the dashboard:

- /dashboard/storage/…/buckets/…/objects: https://www.coritan.com/dashboard/storage
- /dashboard/storage/…/buckets/…/settings: https://www.coritan.com/dashboard/storage

*Versioning* keeps the copy of an object that an overwrite or a delete replaces, so you can bring back a file that was overwritten or deleted by mistake. Each copy is a *version*. Every version you keep counts toward the storage you pay for.

You turn versioning on when you create a bucket, or later in the bucket's **Settings** tab. Once it is on, you cannot turn it off. You can only suspend it.

## Before you begin

- Sign in to the [dashboard](https://www.coritan.com/dashboard/storage) and open the service. The service must be `active` to change versioning and to restore or delete versions. You can list versions and read their details in any status.
- Older versions are billed as stored data until you delete them ([Versions and your bill](#billing)). A lifecycle rule can delete them for you after a number of days ([Rules on a bucket that keeps versions](/docs/object-storage/lifecycle-rules/#versions)).

## How versioning works {#how-it-works}

A bucket's versioning is **Off**, **On** or **Suspended**:

**Off**
: The default. An upload replaces an object that has the same key, and a delete destroys the object. Nothing is kept.

**On**
: An upload to a key that exists makes a new *current version*, and the one it replaces stays as an *older version*. A delete adds a *delete marker*: the object leaves the listing, and every version stays under the marker. Remove the marker and the object is back.

**Suspended**
: The bucket keeps the versions it holds, and you can still restore or delete them. New uploads and deletes keep no copy of what they replace.

Each version has a *version ID*. An object written while versioning was off has the ID `null`.

On a bucket that keeps versions, **Delete object…** and the other deletes in the object list add a delete marker instead of destroying the object. The dialog says so. To destroy data for good, delete the versions themselves ([Delete a version for good](#delete-a-version)).

## Turn versioning on

To turn it on as you create a bucket:

1. Open the service, select the **Buckets** tab, then select **Create bucket…**.
2. Fill in **Name** and choose the **Region** ([Create a bucket](/docs/object-storage/buckets/#create-a-bucket)).
3. Tick **Keep versions**.
4. Select **Create bucket**.

**Object Lock** turns versioning on as well, so ticking it ticks **Keep versions** and greys it out ([Protect objects with Object Lock](/docs/object-storage/object-lock/)).

To turn it on for a bucket you have:

1. Open the service, select the **Buckets** tab, then select the bucket. You can also select **Settings** in the bucket's menu.
2. Select the **Settings** tab. The **Versioning and Object Lock** card shows **Versioning** as **Off**.
3. Select **Turn on versioning…**, then **Turn on versioning** to confirm.

A message confirms it, such as `Versioning is on for u7-assets.` The bucket's header shows **Versioning on**.

## Suspend versioning

1. On the bucket's **Settings** tab, select **Suspend versioning…**.
2. Select **Suspend versioning** to confirm.

A message confirms it, such as `Versioning is suspended for u7-assets.` The header shows **Versioning suspended**. To start keeping versions again, select **Turn versioning back on…**, then **Turn versioning back on**.

A bucket with Object Lock keeps versioning on, so it has no **Suspend versioning…** button.

## List versions {#list-versions}

1. Open the bucket and select the **Objects** tab.
2. Turn on **Show versions** at the top of the **Objects** card. The card is now called **Versions**.

The list shows every version and delete marker in the folder, newest first under each key:

**Name**
: The object's name on its newest row, then **Older version** on each row below it. **Current** marks the current version, and **Delete marker** marks a delete marker.

**Version ID**
: The start of the version ID. Hover over it to see all of it.

**Size** and **Modified**
: The version's size, and when it was written. A delete marker has no size.

The list shows 200 versions at a time. Select **Load more** for the next 200. **Show versions** stays on as you open folders, and it is part of the page address, so the back button and bookmarks keep it. While it is on, **Upload…** and **New folder…** are hidden. Turn it off to upload.

**Show versions** appears only on a bucket whose versioning is on or suspended.

## See a version's details {#details}

Select an object's name, or **Details** in its menu. A panel opens for that version:

- **Object**: the key, size, type, when it was modified, its ETag, and its **Version** ID with a button to copy it.
- **Metadata** and **Tags**, when it has any.
- **Object Lock**, on a bucket with Object Lock ([Protect objects with Object Lock](/docs/object-storage/object-lock/)).
- **Versions**: the key's versions and delete markers, newest first, up to 50. **Showing** marks the version the panel shows. Select another date to open that version.

An older version shows `This is an older version` at the top.

## Restore an older version {#restore}

1. Turn on **Show versions**, open the older version's menu and select **Restore this version…**. In the details panel, the same button is at the bottom.
2. Select **Restore version** to confirm.

We copy the version over the current one, with its metadata and tags. The copy becomes the current version, and the version it replaced stays in the list. A message confirms it, such as `team.jpg restored. The copy is the current version now.`

The dashboard restores versions of up to 5 GB. For a larger version, download it with an S3 client and upload it again, which makes it the current version:

```bash
aws --profile coritan s3api get-object --bucket u7-assets --key videos/launch.mp4 \
  --version-id 3sL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY launch.mp4
aws --profile coritan s3 cp launch.mp4 s3://u7-assets/videos/launch.mp4
```

## Bring back a deleted object {#undelete}

While versioning is on, a delete adds a delete marker and keeps the object's versions. To bring the object back:

1. Turn on **Show versions** and open the object's folder.
2. Open the menu of the row marked **Delete marker** and select **Remove delete marker…**.
3. Select **Remove delete marker** to confirm.

The version under the marker becomes the current version again. A message confirms it, such as `Delete marker removed. old-banner.jpg is back.`

## Delete a version for good {#delete-a-version}

> [!CAUTION]
> Deleting a version destroys it. There is no undo.

1. Turn on **Show versions**, open the version's menu and select **Delete version…**. In the details panel, the same button is at the bottom.
2. Type the object's name, which is the part of its key after the last `/`, such as `team.jpg`.
3. Select **Delete version**.

A message confirms it, such as `That version of team.jpg is deleted.` When you delete the current version, the next newest version becomes current.

On a bucket with Object Lock, a version under retention or a legal hold cannot be deleted this way ([Delete a locked version](/docs/object-storage/object-lock/#delete-locked)).

## Download an older version {#download}

Turn on **Show versions**, open the older version's menu and select **Download**. In the details panel, **Download** downloads the version the panel shows.

Through the API, ask `POST /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/presign` with `{"key": "photos/team.jpg", "op": "get", "version_id": "<version ID>"}`. The answer's `url` downloads that version, and its `version_id` names it.

To download an older version with an S3 client, copy its ID from the details panel and pass it with `--version-id`:

```bash
aws --profile coritan s3api get-object --bucket u7-assets --key photos/team.jpg \
  --version-id 3sL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY team.jpg
```

## Versions and your bill {#billing}

- Every version you keep is stored data and is billed as an object of its size would be ([How Object Storage is billed](/docs/object-storage/usage-and-billing/)). A 10 MB file that you overwrite 9 times stores 100 MB.
- Delete markers hold no data.
- On the **Buckets** tab, **Size** counts every version and **Objects** counts only current objects. Both come from the hourly measurement.
- To stop older versions adding up, delete the ones you no longer need, or add a lifecycle rule with **Delete old versions** ([Lifecycle rules](/docs/object-storage/lifecycle-rules/#versions)).

## Use versions with an S3 client {#s3-clients}

An access key with **Read and write** permissions, whose scope covers the bucket, can:

- list versions with `aws s3api list-object-versions`;
- read a version with `get-object` and `--version-id`;
- delete a version with `delete-object` and `--version-id`, unless Object Lock protects it.

`put-bucket-versioning` answers `403` `AccessDenied` with every access key. Turn versioning on or suspend it in the **Settings** tab or through the API ([S3 compatibility](/docs/object-storage/s3-compatibility/#bucket-settings)).

```bash
aws --profile coritan s3api list-object-versions --bucket u7-assets --prefix photos/team.jpg
```

A `delete-object` without `--version-id` adds a delete marker, as a delete in the dashboard does.

## Delete a bucket that keeps versions

Deleting a bucket destroys every version and delete marker in it. A bucket with no current objects can still hold older versions and delete markers, so its delete dialog offers `Destroy any older versions and delete markers in it`. Tick it, or we refuse the delete while any remain ([Delete a bucket](/docs/object-storage/buckets/#delete-a-bucket)).

## Result

While versioning is on, the bucket's header shows **Versioning on**, the **Objects** tab has **Show versions**, and every overwrite and delete keeps what it replaced. The bucket's size on the **Buckets** tab includes its older versions from the next hourly measurement.

## Troubleshooting

**Show versions** is missing
: The bucket's versioning is off. Turn it on in the bucket's **Settings** tab.

**Turn on versioning…** and **Suspend versioning…** are missing
: The service is not `active`, or the bucket has Object Lock, which keeps versioning on.

`This version is not there` with `It may have been deleted, or it is a delete marker.`
: The version was deleted after the list loaded, or the row is a delete marker, which holds no object. Select the refresh button on the **Objects** card.

`This version is larger than 5 GB, which is the most we copy in one request. Copy it with an S3 client instead.`
: Download the version with an S3 client and upload it again ([Restore an older version](#restore)).

`u7-assets does not keep versions, so there is nothing to restore.`
: The bucket's versioning is off. Only a bucket whose versioning is on or suspended has versions to restore.

The bucket's size went up after you turned versioning on
: Each overwrite and delete now keeps a version, and versions are stored data. Delete versions you do not need, or add a lifecycle rule ([Versions and your bill](#billing)).

`u7-assets still holds object versions. Delete it with force to destroy every version as well.`
: The bucket has older versions or delete markers. Tick the box in the delete dialog, or send `force=true` through the API.

`Too many requests for this action. Try again later.`
: Your account made 300 versioning, retention and version changes in the last hour. Wait, then try again.

`This service is suspended; it can be changed once it is active`
: Pay the overdue invoice ([Failed payments and suspended services](/docs/billing/failed-payments/)), then try again. You can still list versions and read their details.

## Related

- [Protect objects with Object Lock](/docs/object-storage/object-lock/)
- [Upload, download and delete objects](/docs/object-storage/objects/)
- [Delete objects on a schedule with lifecycle rules](/docs/object-storage/lifecycle-rules/)
- [How Object Storage is billed](/docs/object-storage/usage-and-billing/)

## With the API

Each request takes the service ID and the bucket ID, from [`GET /api/v1/client/object-storage/{service_id}/buckets`](/docs/api/reference/client/object-storage/object-storage-buckets/#op-get-api-v1-client-object-storage-service-id-buckets). A service that is not Object Storage on your account answers `404` with `Object storage service not found`, and a bucket that is not on the service answers `404` with `Bucket not found`.

Each bucket in the list carries `versioning`, which is `off`, `enabled` or `suspended`, and `object_lock` ([Protect objects with Object Lock](/docs/object-storage/object-lock/#with-the-api)). To create a bucket that keeps versions, add `"versioning": true` to [`POST /api/v1/client/object-storage/{service_id}/buckets`](/docs/api/reference/client/object-storage/object-storage-buckets/#op-post-api-v1-client-object-storage-service-id-buckets):

```bash
curl -X POST https://api.coritan.com/api/v1/client/object-storage/1207/buckets \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "assets", "versioning": true}'
```

### Turn versioning on or suspend it

[`PUT /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/versioning`](/docs/api/reference/client/object-storage/object-storage-buckets/#op-put-api-v1-client-object-storage-service-id-buckets-bucket-id-versioning) takes `status`, which is `enabled` or `suspended`:

```bash
curl -X PUT https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/versioning \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "enabled"}'
```

```json
{
  "bucket_id": 31,
  "versioning": "enabled",
  "object_lock": {"enabled": false, "mode": null, "days": null, "years": null}
}
```

There is no `off`: any other `status` answers `422`.

### List versions

[`GET /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/versions`](/docs/api/reference/client/object-storage/object-storage-buckets/#op-get-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-versions) lists one page of versions and delete markers, by key and newest first. It takes these query parameters:

`prefix`
: Only keys that start with it, up to 1024 characters. Give a folder with its `/`, such as `photos/`.

`max_keys`
: How many versions and delete markers a page holds, from 1 to 1000. The default is 100.

`key_marker` and `version_marker`
: The `next_key_marker` and `next_version_marker` of the previous page, to read the next one.

`flat`
: `true` lists every key under the prefix, with no folders. The default, `false`, lists the folders under the prefix in `prefixes`.

```bash
curl "https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/objects/versions?prefix=photos/" \
  -H "Authorization: Bearer $CORITAN_TOKEN"
```

```json
{
  "items": [
    {"key": "photos/old-banner.jpg", "version_id": "Kq2vO0n4yPnBh7x1VxN3w5Wq9cAZr6sT", "is_latest": true, "is_delete_marker": true, "size": null, "last_modified": "2026-09-21T08:15:40+00:00", "etag": null},
    {"key": "photos/old-banner.jpg", "version_id": "null", "is_latest": false, "is_delete_marker": false, "size": 90211, "last_modified": "2026-08-30T10:02:00+00:00", "etag": "1f3870be274f6c49b3e31a0c6728957f"},
    {"key": "photos/team.jpg", "version_id": "3sL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY", "is_latest": true, "is_delete_marker": false, "size": 482113, "last_modified": "2026-09-20T14:02:11+00:00", "etag": "9b2cf535f27731c974343645a3985328"}
  ],
  "prefixes": ["photos/2026/"],
  "prefix": "photos/",
  "next_key_marker": null,
  "next_version_marker": null
}
```

`is_latest` marks each key's current version or delete marker. `size` is in bytes, and `null` for a delete marker. The markers are `null` on the last page. On a bucket that never kept versions, each object is listed once with `version_id` `null`.

### Read a version's details {#api-details}

[`GET /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/details`](/docs/api/reference/client/object-storage/object-storage-buckets/#op-get-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-details) takes `key` and, for a version other than the current one, `version_id`:

```bash
curl "https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/objects/details?key=photos/team.jpg" \
  -H "Authorization: Bearer $CORITAN_TOKEN"
```

```json
{
  "key": "photos/team.jpg",
  "version_id": "3sL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY",
  "is_latest": true,
  "size": 482113,
  "content_type": "image/jpeg",
  "etag": "9b2cf535f27731c974343645a3985328",
  "last_modified": "2026-09-20T14:02:11+00:00",
  "metadata": {"author": "studio"},
  "tags": [{"key": "team", "value": "marketing"}],
  "retention": null,
  "legal_hold": false,
  "object_lock": false,
  "versioning": "enabled",
  "versions": [
    {"key": "photos/team.jpg", "version_id": "3sL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY", "is_latest": true, "is_delete_marker": false, "size": 482113, "last_modified": "2026-09-20T14:02:11+00:00", "etag": "9b2cf535f27731c974343645a3985328"},
    {"key": "photos/team.jpg", "version_id": "hX0p8Y2kW1cS7fRbN4eD6gA9vLmQ3tZu", "is_latest": false, "is_delete_marker": false, "size": 455020, "last_modified": "2026-09-02T09:40:27+00:00", "etag": "c4ca4238a0b923820dcc509a6f75849b"}
  ],
  "versions_truncated": false
}
```

`metadata` holds the object's `x-amz-meta-*` values. `versions` lists the key's versions and delete markers, newest first, up to 50, and `versions_truncated` is `true` when there are more. `retention` and `legal_hold` are described in [Protect objects with Object Lock](/docs/object-storage/object-lock/#with-the-api). A key or version that does not exist, or a version that is a delete marker, answers `404`.

### Restore a version

[`POST /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/restore`](/docs/api/reference/client/object-storage/object-storage-buckets/#op-post-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-restore) copies a version over the current one, with its metadata and tags:

```bash
curl -X POST https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/objects/restore \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key": "photos/team.jpg", "version_id": "hX0p8Y2kW1cS7fRbN4eD6gA9vLmQ3tZu"}'
```

```json
{"ok": true, "key": "photos/team.jpg", "restored_from": "hX0p8Y2kW1cS7fRbN4eD6gA9vLmQ3tZu", "version_id": "Vb7nR2qP5tL0wE3yU8iO1aS4dF6gH9jK"}
```

`version_id` is the ID of the new current version.

### Delete a version or a delete marker

[`DELETE /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/version`](/docs/api/reference/client/object-storage/object-storage-buckets/#op-delete-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-version) deletes one version or delete marker for good. The body takes `key` and `version_id`:

```bash
curl -X DELETE https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/objects/version \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key": "photos/old-banner.jpg", "version_id": "Kq2vO0n4yPnBh7x1VxN3w5Wq9cAZr6sT"}'
```

```json
{"ok": true, "key": "photos/old-banner.jpg", "version_id": "Kq2vO0n4yPnBh7x1VxN3w5Wq9cAZr6sT", "delete_marker": true}
```

`delete_marker` is `true` when what you removed was a delete marker, which brings back the version under it. On a bucket with Object Lock, a locked version takes `bypass_governance` and `confirm` as well ([Delete a locked version](/docs/object-storage/object-lock/#delete-locked)).

### Errors

Where `detail` is an object, `error` is a code your program can test and `message` is a sentence you can show.

| Status | `detail` | Cause |
| --- | --- | --- |
| `404` | An object with `"error": "version_not_found"` | The key or version does not exist, or the version is a delete marker. |
| `409` | An object with `"error": "versioning_refused"` | You asked to suspend versioning on a bucket with Object Lock, or to restore on a bucket that does not keep versions. |
| `409` | An object with `"error": "version_locked"` | Object Lock protects the version ([Delete a locked version](/docs/object-storage/object-lock/#delete-locked)). |
| `409` | `This service is pending; it can be changed once it is active` | The service is not `active`. The message names its status. |
| `422` | An object with `"error": "too_large_to_restore"` | The version is larger than 5 GB. |
| `422` | A list of fields | The body or a query parameter is not in the shape above. |
| `429` | An object with `"error": "rate_limited"` and `retry_after_seconds` | Your account made 300 versioning, retention and version changes in the last hour. The `Retry-After` header says how long to wait. |
| `502` | A message that starts `Could not`, such as `Could not restore photos/team.jpg:` and a reason | The bucket's region refused the request. Try again in a minute. |

## API

- `PUT /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/versioning`: Turn versioning on for a bucket, or suspend it (https://www.coritan.com/docs/api/reference/client/object-storage/object-storage-buckets/#op-put-api-v1-client-object-storage-service-id-buckets-bucket-id-versioning)
- `GET /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/versions`: List object versions (https://www.coritan.com/docs/api/reference/client/object-storage/object-storage-buckets/#op-get-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-versions)
- `GET /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/details`: Get object details (https://www.coritan.com/docs/api/reference/client/object-storage/object-storage-buckets/#op-get-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-details)
- `POST /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/restore`: Restore object version (https://www.coritan.com/docs/api/reference/client/object-storage/object-storage-buckets/#op-post-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-restore)
- `DELETE /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/objects/version`: Delete one version or delete marker for good (https://www.coritan.com/docs/api/reference/client/object-storage/object-storage-buckets/#op-delete-api-v1-client-object-storage-service-id-buckets-bucket-id-objects-version)
