> ## 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 Google Cloud Storage

> Configure Google Cloud Storage for your downloads

## Overview

Set up Google Cloud Storage for uploaded videos. Authenticate with a service account JSON key. 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="project_id" type="string" required>
  Google Cloud project ID
</ParamField>

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

<ParamField body="service_account_json" type="string" required>
  Service account JSON credentials (as escaped string or Base64 encoded)
</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: `gcs`
</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 cURL theme={null}
  curl -X POST "https://api.tornadoapi.io/user/gcs" \
    -H "x-api-key: sk_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "project_id": "my-gcp-project",
      "bucket": "tornado-downloads",
      "service_account_json": "{\"type\":\"service_account\",\"project_id\":\"my-gcp-project\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\",\"client_email\":\"tornado@my-gcp-project.iam.gserviceaccount.com\"}"
    }'
  ```

  ```python Python (from file) theme={null}
  import requests
  import json

  # Load service account JSON file (recommended)
  with open("service-account.json") as f:
      sa_json = json.dumps(json.load(f))

  response = requests.post(
      "https://api.tornadoapi.io/user/gcs",
      headers={"x-api-key": "sk_your_api_key"},
      json={
          "project_id": "my-gcp-project",
          "bucket": "tornado-downloads",
          "service_account_json": sa_json
      }
  )
  print(response.json())
  ```

  ```python Python (inline) theme={null}
  import requests
  import json

  # Build as a dict and serialize with json.dumps()
  service_account = {
      "type": "service_account",
      "project_id": "my-gcp-project",
      "private_key_id": "key-id",
      "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
      "client_email": "tornado@my-gcp-project.iam.gserviceaccount.com",
      "client_id": "123456789",
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token"
  }

  response = requests.post(
      "https://api.tornadoapi.io/user/gcs",
      headers={"x-api-key": "sk_your_api_key"},
      json={
          "project_id": "my-gcp-project",
          "bucket": "tornado-downloads",
          "service_account_json": json.dumps(service_account)
      }
  )
  print(response.json())
  ```

  ```json Response theme={null}
  {
    "message": "Google Cloud Storage configured successfully",
    "provider": "gcs",
    "container_or_bucket": "tornado-downloads"
  }
  ```
</CodeGroup>

## Success Response

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "message": "Google Cloud Storage configured successfully",
    "provider": "gcs",
    "container_or_bucket": "tornado-downloads"
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 400 Bad Request - Invalid JSON theme={null}
  {
    "error": "Invalid service account JSON: missing 'private_key' field"
  }
  ```

  ```json 400 Bad Request - Invalid Credentials theme={null}
  {
    "error": "Credential validation failed: AuthenticationFailed"
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": "Invalid API Key"
  }
  ```

  ```json 503 Service Unavailable theme={null}
  {
    "error": "GCS storage configuration is temporarily unavailable"
  }
  ```
</ResponseExample>

## Verification Process

When you submit storage configuration, Tornado:

1. Validates the request format and the service account JSON structure
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

## Required GCS Permissions

The service account needs the **Storage Object Admin** role, which includes:

* `storage.objects.create`
* `storage.objects.delete`
* `storage.objects.get`
* `storage.objects.list`

<Tip>
  For the `service_account_json` field, you can either:

  * Pass the JSON as an escaped string
  * Base64 encode the JSON file: `base64 -w0 service-account.json`
</Tip>

<Warning>
  **Python users:** Do not manually construct the `service_account_json` as a raw string with escaped quotes.
  The `\n` characters in the private key will be interpreted as literal newlines by Python, which produces
  invalid JSON and causes a `"Invalid JSON in service account credentials"` error.

  Always use `json.dumps()` on a dict or a loaded JSON file to ensure proper escaping:

  ```python theme={null}
  # Correct: json.dumps() handles escaping
  sa_json = json.dumps({"type": "service_account", "private_key": "-----BEGIN...\n...", ...})

  # Wrong: manual string with \n becomes literal newlines
  sa_json = '{"type": "service_account", "private_key": "-----BEGIN...\n..."}'
  ```
</Warning>
