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

# Configure S3 Storage

> Configure S3-compatible storage (AWS S3, Cloudflare R2, MinIO, etc.)

## Overview

Set up your own S3-compatible storage for uploaded videos. Credentials are verified before saving.

## Header Parameters

<ParamField header="x-api-key" type="string" required>
  Your API key for authentication
</ParamField>

## Request Body

<ParamField body="endpoint" type="string" required>
  S3 endpoint URL (e.g., `https://s3.us-east-1.amazonaws.com` or `https://ACCOUNT_ID.r2.cloudflarestorage.com`)
</ParamField>

<ParamField body="bucket" type="string" required>
  Bucket name
</ParamField>

<ParamField body="region" type="string" required>
  AWS region (e.g., `us-east-1`, `auto` for R2)
</ParamField>

<ParamField body="access_key" type="string" required>
  AWS Access Key ID or equivalent
</ParamField>

<ParamField body="secret_key" type="string" required>
  AWS Secret Access Key or equivalent
</ParamField>

<ParamField body="folder_prefix" type="string">
  Optional folder prefix for organizing uploads (e.g., `downloads/2024/`)
</ParamField>

<ParamField body="base_folder" type="string" default="videos">
  Base folder name for uploaded files. Defaults to `videos` if not specified. Set to a custom value to change the top-level folder where files are stored (e.g., `downloads`, `media`).
</ParamField>

## Response

<ResponseField name="message" type="string">
  Success confirmation message
</ResponseField>

<ResponseField name="provider" type="string">
  Storage provider type: `s3`
</ResponseField>

<ResponseField name="container_or_bucket" type="string">
  The configured bucket name
</ResponseField>

<ResponseField name="folder_prefix" type="string">
  The configured folder prefix (if provided)
</ResponseField>

## Examples

<CodeGroup>
  ```bash AWS S3 theme={null}
  curl -X POST "https://api.tornadoapi.io/user/s3" \
    -H "x-api-key: sk_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "endpoint": "https://s3.us-east-1.amazonaws.com",
      "bucket": "my-tornado-downloads",
      "region": "us-east-1",
      "access_key": "AKIAIOSFODNN7EXAMPLE",
      "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
      "folder_prefix": "videos/"
    }'
  ```

  ```bash Cloudflare R2 theme={null}
  curl -X POST "https://api.tornadoapi.io/user/s3" \
    -H "x-api-key: sk_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "endpoint": "https://ACCOUNT_ID.r2.cloudflarestorage.com",
      "bucket": "my-downloads",
      "region": "auto",
      "access_key": "R2_ACCESS_KEY_ID",
      "secret_key": "R2_SECRET_ACCESS_KEY"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.tornadoapi.io/user/s3",
      headers={"x-api-key": "sk_your_api_key"},
      json={
          "endpoint": "https://s3.us-east-1.amazonaws.com",
          "bucket": "my-tornado-downloads",
          "region": "us-east-1",
          "access_key": "AKIAIOSFODNN7EXAMPLE",
          "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
      }
  )
  print(response.json())
  ```

  ```json Response theme={null}
  {
    "message": "S3 storage configured successfully",
    "provider": "s3",
    "container_or_bucket": "my-tornado-downloads",
    "folder_prefix": "videos/"
  }
  ```
</CodeGroup>

## Success Response

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "message": "S3 storage configured successfully",
    "provider": "s3",
    "container_or_bucket": "my-tornado-downloads",
    "folder_prefix": "videos/"
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 400 Bad Request - Invalid Credentials theme={null}
  {
    "error": "Credential validation failed: Access Denied"
  }
  ```

  ```json 400 Bad Request - Bucket Not Found theme={null}
  {
    "error": "Credential validation failed: NoSuchBucket"
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": "Invalid API Key"
  }
  ```
</ResponseExample>

## Verification Process

When you submit storage configuration, Tornado:

1. Validates the request format and required fields
2. Creates a storage client with your credentials
3. Attempts to upload a small test file (`verify_credentials.txt`)
4. Deletes the test file
5. If successful, saves the configuration encrypted

<Warning>
  Ensure your credentials have both **read and write** permissions before configuring.
</Warning>

## Required IAM Permissions

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

## Supported S3-Compatible Providers

| Provider            | Endpoint Format                                 | Region      |
| ------------------- | ----------------------------------------------- | ----------- |
| AWS S3              | `https://s3.{region}.amazonaws.com`             | Your region |
| Cloudflare R2       | `https://{account_id}.r2.cloudflarestorage.com` | `auto`      |
| DigitalOcean Spaces | `https://{region}.digitaloceanspaces.com`       | Your region |
| Backblaze B2        | `https://s3.{region}.backblazeb2.com`           | Your region |
| Wasabi              | `https://s3.{region}.wasabisys.com`             | Your region |
| MinIO               | Your MinIO URL                                  | Your region |
| OVH Object Storage  | `https://s3.{region}.cloud.ovh.net`             | Your region |
