> ## Documentation Index
> Fetch the complete documentation index at: https://docs.knoxcall.com/llms.txt
> Use this file to discover all available pages before exploring further.

# S3 Body Archival

> Archive proxied request and response bodies to Amazon S3, Cloudflare R2 or MinIO — the bucket, a least-privilege IAM user (no ListBucket), the region, bucket and endpoint fields, and the deployment flag that switches archival on.

# S3 Body Archival

Large or file-typed request and response bodies are offloaded to object storage instead of Postgres, for compliance and audit retention and to keep the dashboard responsive. Any S3-compatible store works: Amazon S3, Cloudflare R2, MinIO, DigitalOcean Spaces.

**Scope: Global defaults.** Archival runs on the platform credential. On KnoxCall Cloud it is KnoxCall's bucket and there is nothing to configure; on a self-hosted deployment save your bucket under **Global defaults**. The card is also offered under *This tenant*, but a workspace-scoped S3 row is not used for archival today — save it at platform scope.

## 1. In AWS (or your S3-compatible provider)

1. **Create the bucket first**, with **Block Public Access** on and default encryption enabled. Note its region.
2. **IAM → Users → Create user**, *without* console access.
3. Attach an inline policy granting **object operations only** on that one bucket:

```json theme={"dark"}
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
    "Resource": "arn:aws:s3:::<bucket>/*"
  }]
}
```

`s3:ListBucket` is **not** needed — KnoxCall addresses objects by key and never enumerates the bucket. No other service, no other bucket.

4. **Security credentials → Create access key → Application running outside AWS**. Copy both halves.

**Cloudflare R2:** R2 → *Manage R2 API tokens* → a token with *Object Read & Write* on the one bucket; the endpoint is `https://<account id>.r2.cloudflarestorage.com` and the region is `auto`. **MinIO:** a service account scoped to the bucket; the endpoint is your MinIO URL.

## 2. Enter it in KnoxCall

**Settings → Integrations → Global defaults → S3** → *Configure*:

| Field                   | Value                                                                       |
| ----------------------- | --------------------------------------------------------------------------- |
| **Access key ID**       | `AKIA…` (or the R2/MinIO key id)                                            |
| **Secret access key**   | The secret half                                                             |
| **Region**              | The bucket's region, e.g. `eu-west-1`; `auto` for R2                        |
| **Bucket**              | The bucket name                                                             |
| **Endpoint (R2/MinIO)** | Only for a non-AWS store, e.g. `https://<account>.r2.cloudflarestorage.com` |

**Save credentials.**

## 3. Switch archival on (self-hosted)

The credential is *where* bodies go; a deployment flag decides *whether* they go. On a self-hosted host set in the server environment:

```bash theme={"dark"}
S3_RESPONSE_STORAGE_ENABLED=true
# optional: S3_RESPONSE_MAX_FILE_SIZE (bytes, default 10 MB), S3_LARGE_BODY_THRESHOLD (bytes, default 10 KB)
```

Bodies larger than the threshold, and file-typed bodies, are archived; the log entry keeps a reference and the dashboard fetches on demand.

## 4. Verify

Proxy a request with a large JSON response and open it under **API logs** — the body loads from the archive. On the store side, objects appear under an environment-named prefix.

## Things that bite

* **Both key halves, or nothing.** With one half stored the row is ignored and a self-hosted host falls back to the `AWS_S3_*` environment variables, or to no archival.
* **Region mismatch** produces a `301` on every write. The region must be the bucket's.
* **Storing the key does not turn archival on.** Without the flag the row is inert.
* **Deletion and retention** are KnoxCall's: the retention purge deletes archived objects when it deletes the log rows they belong to, which is why `DeleteObject` is in the policy.

## Related

* [API logs](/monitoring/api-logs)
* [Audit logs](/monitoring/audit-logs)
