# GetObjectRetention

GetObjectRetention returns the retention of an object version: its mode, governance or
compliance, and the date until which it is locked. Without `versionId` it reads the current
version. Retention exists only in a bucket with [Object Lock](https://docs.strg.com/object-locks) enabled, and
setting or extending one is the job of [PutObjectRetention](https://docs.strg.com/s3-api/PutObjectRetention).

**Info: Good to know on STRG**

- Reading a retention takes the *Read retention* permission on the access key.
- `x-amz-expected-bucket-owner` is welcome and has nothing to check: a key only ever reaches
  buckets of its own account.
- A key containing a `.` or `..` path segment (`a/../b`) cannot be put in a URL, so STRG
  answers `400 InvalidURI` for it.

## When you'd use it

- **Checking a backup is really protected.** A nightly backup job that uploads with a retention
  can read it back afterwards, confirming each version is locked in the mode and until the date
  it expects.
- **Audits and compliance reports.** A script preparing a records report reads the retain-until
  date of every version it lists, so the report says exactly how long each one is kept.
- **Before a clean-up.** A job about to delete old versions can first learn which of them are
  still locked, and until when, rather than finding out from a failed delete.
- **An agent explaining what it sees.** An agent asked why a file cannot be removed reads its
  retention and can tell the person the mode and the date the lock ends.

## Request

```http
GET /photos/2024/cat.jpg?retention HTTP/1.1
Host: my-bucket.s3.eu-central-1.strg.com
Authorization: AWS4-HMAC-SHA256 Credential=...
x-amz-date: 20260926T120000Z
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```

The path-style form is `GET /my-bucket/photos/2024/cat.jpg?retention` on
`s3.eu-central-1.strg.com`, which a bucket name containing a dot needs -
[Addressing](https://docs.strg.com/s3-api/overview#addressing) explains both.

### Permissions

The access key needs **Read retention** (`object-retention:read`). For a key limited to certain
buckets or to a key prefix, the bucket and the object key are within them.
[Access keys](https://docs.strg.com/access-keys) has the details.

### URI parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Bucket` | String | Yes | The bucket holding the object. |
| `Key` | String | Yes | The object key. A key containing a `.` or `..` path segment answers `400 InvalidURI`. |
| `retention` | - | Yes | Selects this operation. Takes no value. |
| `versionId` | String | No | The version whose retention to read; without it, the current version's. A version ID is an opaque string. |

Any other query parameter is answered with `501 NotImplemented`, so a client knows straight
away. The `X-Amz-*` parameters of a presigned URL and the `x-id` parameter the JavaScript SDK
adds are welcome.

### Request headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `x-amz-expected-bucket-owner` | String | No | Welcome, with nothing to check: an access key only ever reaches its own account's buckets. |
| `x-amz-request-payer` | String | No | Not applicable: STRG has no Requester Pays buckets. |

### Request body

None.

## Response

A successful request answers `200 OK` with the retention in the body.

### Response headers

| Name | Description |
| --- | --- |
| `x-amz-request-id` | The request ID, handy for a support request. |

### Response body

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Mode>GOVERNANCE</Mode>
  <RetainUntilDate>2026-12-31T00:00:00.000Z</RetainUntilDate>
</Retention>
```

| Element | Type | Description |
| --- | --- | --- |
| `Retention` | Container | The object version's retention. |
| `Mode` | String | `GOVERNANCE` or `COMPLIANCE`. [How it works](https://docs.strg.com/object-locks#how-it-works) explains what each allows. |
| `RetainUntilDate` | Timestamp | The date and time, in UTC, until which the version cannot be deleted permanently. |

## Errors

These are the errors this request can return, and the situation behind each one.

| Code | HTTP status | When |
| --- | --- | --- |
| `InvalidRequest` | 400 | The bucket does not have Object Lock enabled, so no object in it has a retention to read. |
| `NotImplemented` | 501 | The request carries a query parameter other than `retention` and `versionId` (presigning's `X-Amz-*` parameters and `x-id` aside). |
| `NoSuchBucket` | 404 | The bucket does not exist, it is in a region other than the endpoint's, or the account has no storage in this endpoint's region. |
| `NoSuchKey` | 404 | The object does not exist. |
| `AccessDenied` | 403 | The access key lacks *Read retention*, or the bucket or key is outside its buckets or prefix. |
| `InvalidAccessKeyId` | 403 | The access key does not exist, has expired or been revoked - or it was created moments ago and is not usable yet in a region where the account already stores data, in which case the same request works shortly afterwards. |
| `InvalidURI` | 400 | The key contains a `.` or `..` path segment. |
| `SignatureDoesNotMatch` | 403 | The request signature does not match. |
| `RequestTimeTooSkewed` | 403 | The request time is more than 15 minutes from the server's. |
| `AuthorizationHeaderMalformed` | 400 | The signature's credential scope names a region other than the endpoint's. |
| `SlowDown` | 503 | Too many requests. The `Retry-After` header says when a retry is welcome; the AWS SDKs retry on their own. |
| `ServiceUnavailable` | 503 | A temporary failure; the same request can be retried. |
| `InternalError` | 500 | An unexpected failure; the same request can be retried. |

## Differences from Amazon S3

The table lists each place where STRG behaves differently, and what that means for a client.

| Aspect | Amazon S3 | STRG |
| --- | --- | --- |
| Permissions | `s3:GetObjectRetention` | *Read retention* (`object-retention:read`) on the access key |
| `x-amz-expected-bucket-owner` | Checked against the bucket owner | Welcome, with nothing to check |
| Bucket in another region | `301` redirect with `x-amz-bucket-region` | `404 NoSuchBucket`. A bucket is reached through its own regional endpoint |
| Nothing stored in the endpoint's region yet | Any valid key reaches the endpoint | `404 NoSuchBucket`, since the account has no buckets there |
| Keys with `.` or `..` segments | Allowed | `400 InvalidURI`, because such a key cannot be put in a URL. [DeleteObjects](https://docs.strg.com/s3-api/DeleteObjects) can still delete one |

## Examples

Reading the retention of the current version of `photos/2024/cat.jpg`:

**AWS CLI**

```bash
aws s3api get-object-retention \
  --bucket my-bucket \
  --key photos/2024/cat.jpg \
  --endpoint-url https://s3.eu-central-1.strg.com \
  --region eu-central-1
```

**Python (boto3)**

```python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.eu-central-1.strg.com",
    region_name="eu-central-1",
)

response = s3.get_object_retention(Bucket="my-bucket", Key="photos/2024/cat.jpg")
retention = response["Retention"]
print(retention["Mode"], retention["RetainUntilDate"])
```

**JavaScript (AWS SDK v3)**

```javascript
import { GetObjectRetentionCommand, S3Client } from "@aws-sdk/client-s3";

const client = new S3Client({
  endpoint: "https://s3.eu-central-1.strg.com",
  region: "eu-central-1",
});

const response = await client.send(
  new GetObjectRetentionCommand({
    Bucket: "my-bucket",
    Key: "photos/2024/cat.jpg",
  }),
);
console.log(response.Retention?.Mode, response.Retention?.RetainUntilDate);
```

Reading the retention of a specific version, with `VERSION_ID` set to a version ID from
[ListObjectVersions](https://docs.strg.com/s3-api/ListObjectVersions):

**AWS CLI**

```bash
aws s3api get-object-retention \
  --bucket my-bucket \
  --key photos/2024/cat.jpg \
  --version-id "$VERSION_ID" \
  --endpoint-url https://s3.eu-central-1.strg.com \
  --region eu-central-1
```

**Python (boto3)**

```python
import os

import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.eu-central-1.strg.com",
    region_name="eu-central-1",
)

response = s3.get_object_retention(
    Bucket="my-bucket",
    Key="photos/2024/cat.jpg",
    VersionId=os.environ["VERSION_ID"],
)
print(response["Retention"])
```

**JavaScript (AWS SDK v3)**

```javascript
import { GetObjectRetentionCommand, S3Client } from "@aws-sdk/client-s3";

const client = new S3Client({
  endpoint: "https://s3.eu-central-1.strg.com",
  region: "eu-central-1",
});

const response = await client.send(
  new GetObjectRetentionCommand({
    Bucket: "my-bucket",
    Key: "photos/2024/cat.jpg",
    VersionId: process.env.VERSION_ID,
  }),
);
console.log(response.Retention);
```

## Related

- [PutObjectRetention](https://docs.strg.com/s3-api/PutObjectRetention)
- [GetObjectLegalHold](https://docs.strg.com/s3-api/GetObjectLegalHold)
- [GetObjectLockConfiguration](https://docs.strg.com/s3-api/GetObjectLockConfiguration)
- [HeadObject](https://docs.strg.com/s3-api/HeadObject)
- [ListObjectVersions](https://docs.strg.com/s3-api/ListObjectVersions)
- [Object Lock](https://docs.strg.com/object-locks)
- [Access keys](https://docs.strg.com/access-keys)
- [S3 API overview](https://docs.strg.com/s3-api/overview)
