> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# Cloud Storage Config

> Step-by-step credentials setup for AWS S3 (access key + secret), Google Cloud Storage (service account JSON), Azure Blob Storage (account name + key), and Cloudflare R2 (account ID + API token).

## Agent Quick Reference - Cloud Storage Credentials

Every indexing request also requires `processing_type` (`"advanced"`, `"basic"` or `"auto"`). Omitting it returns 422.

### AWS S3

* Needs: `aws_access_key_id`, `aws_secret_access_key`, `bucket_region`
* IAM permissions required: `s3:GetObject`, `s3:ListBucket`
* Endpoint: `POST /v2/collections/{name}/index/s3`

### Google Cloud Storage

* Needs: `service_account_json` (full JSON key file content as string), `bucket_name`
* IAM role required: `roles/storage.objectViewer`
* Endpoint: `POST /v2/collections/{name}/index/gcs`

### Azure Blob Storage

* Needs: `account_name`, `account_key` (base64), `container_name`
* Permissions required: Read + List (or use SAS token)
* Endpoint: `POST /v2/collections/{name}/index/azure`

### Cloudflare R2

* Needs: `account_id`, `access_key_id`, `secret_access_key`, `bucket_name`
* Token permissions: Object Read only
* Endpoint: `POST /v2/collections/{name}/index/r2`
* Optional: `jurisdiction` ("default", "eu", "fedramp", "us")

### Supabase

* Needs: `endpoint_url`, `access_key_id`, `secret_access_key`, `bucket_name`, `region`
* S3-compatible: Supabase Storage exposes an S3 endpoint at `https://<project_ref>.storage.supabase.co/storage/v1/s3`
* Endpoint: `POST /v2/collections/{name}/index/supabase`
* Scope to a folder or file with `POST /v2/collections/{name}/index/supabase/directory` (`directory_path`) or `.../index/supabase/file` (`file_uri`)

### Backblaze B2

* Needs: `endpoint_url`, `access_key_id`, `secret_access_key`, `bucket_name`, `region`
* S3-compatible: Backblaze B2 exposes an S3 endpoint at `https://s3.<region>.backblazeb2.com`
* Endpoint: `POST /v2/collections/{name}/index/backblaze`
* Scope to a folder or file with `POST /v2/collections/{name}/index/backblaze/directory` (`directory_path`) or `.../index/backblaze/file` (`file_uri`)

### Dropbox

* Needs: `dropbox_access_token`
* Scopes required: `files.metadata.read`, `files.content.read` (optional: `sharing.write` for clickable `source.uri` links)
* Endpoint: `POST /v2/collections/{name}/index/dropbox` (indexes the entire account, recursive)
* Scope to a folder or file with `POST /v2/collections/{name}/index/dropbox/directory` (`directory_path`) or `.../index/dropbox/file` (`file_path`)

Captain supports indexing from **Amazon S3**, **Google Cloud Storage**, **Azure Blob Storage**, **Cloudflare R2**, **Supabase**, **Backblaze B2**, and **Dropbox**.

This guide walks through the process of connecting them with Captain.

### Choose your cloud storage provider

#### [AWS S3 Buckets](#aws-s3-bucket-setup)

#### [GCP Storage Buckets](#google-cloud-storage-bucket-setup)

#### [Azure Blob Storage](#azure-blob-storage-setup)

#### [Cloudflare R2](#cloudflare-r2-setup)

#### [Supabase Storage](#supabase-setup)

#### [Backblaze B2](#backblaze-b2-setup)

#### [Dropbox](#dropbox-setup)

#### [Google Drive](#google-drive-headless-setup)

---

## AWS S3 Bucket Setup

### Step 1: Create the Bucket

1. Log in to the [AWS Console](https://console.aws.amazon.com/)
2. Navigate to **S3** → **Buckets** → **Create bucket**

   **Captain recommends** the following standard configuration:

   1. Select a region for the bucket
   2. Set a bucket name
   3. Keep **General Purpose Storage** selected if asked
   4. Keep **ACLs disabled**
   5. Keep **Block *all* public access** checked
   6. (Optional) Enable **Bucket Versioning** (for SOC 2 compliance)
   7. Once these settings are configured, the bucket is ready.

      Scroll down and click **Create bucket**

---

## AWS S3 Access Keys

To index files from Amazon S3 buckets, you'll need an AWS Access Key ID and Secret Access Key.

### Step 1: Navigate to IAM Security Credentials

1. Log in to the [AWS Console](https://console.aws.amazon.com/)
2. Click on your account name in the top-right corner
3. Select **Security credentials** from the dropdown menu

![AWS Console - Security Credentials](https://y2i6auvwuaxlu05f.public.blob.vercel-storage.com/aws_image.png)

### Step 2: Create Access Key

1. Scroll down to the **Access keys** section
2. Click the **Create access key** button

![AWS IAM - Create Access Key](https://y2i6auvwuaxlu05f.public.blob.vercel-storage.com/aws_image2.png)

### Step 3: Retrieve Your Credentials

1. Your Access Key ID and Secret Access Key will be displayed
2. **Important**: This is the only time you can view the Secret Access Key
3. Click **Show** to reveal the Secret Access Key
4. Copy both the Access Key ID and Secret Access Key to a secure location
5. Optionally, download the `.csv` file for safekeeping

![AWS - Retrieve Access Key](https://y2i6auvwuaxlu05f.public.blob.vercel-storage.com/aws_image3.png)

### Required IAM Permissions (AWS)

Your AWS access key needs the following permissions to work with Captain:

**For read-only access to S3 buckets:**

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

Replace `your-bucket-name` with your actual S3 bucket name.

---

## Google Cloud Storage Bucket Setup

### Step 1: Create the Bucket

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Navigate to **Cloud Storage** → **Buckets** → **Create**
3. Enter a unique bucket name (e.g., `company-captain-documents`)
4. Select a **Location type** (Region, Dual-region, or Multi-region)
5. Select **Standard** storage class for general use
6. Under **Access control**, select "Uniform" (recommended)
7. Click **Create**

---

## Google Cloud Storage Credentials

To index files from Google Cloud Storage buckets, you'll need a Service Account JSON key.

### Step 1: Navigate to Service Accounts

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Navigate to **IAM & Admin** → **Service Accounts**
3. Click **Create Service Account**

![Google Cloud - Service Accounts](https://y2i6auvwuaxlu05f.public.blob.vercel-storage.com/google_image.png)

### Step 2: Create Service Account

1. Enter a **Service account name** (e.g., `captain-storage-access`)
2. Add a **Service account description** (optional but recommended)
3. The Service account ID will be auto-generated
4. Click **Create and continue**

![Google Cloud - Create Service Account](https://y2i6auvwuaxlu05f.public.blob.vercel-storage.com/google_image2.png)

### Step 3: Grant Permissions

Under **Grant this service account access to project**, choose the appropriate role based on your needs:

**For read-only access to buckets/objects:**

* Role: **Storage Object Viewer** (`roles/storage.objectViewer`)

**For read/write access:**

* Role: **Storage Object Admin** (`roles/storage.objectAdmin`)

**For full bucket management:**

* Role: **Storage Admin** (`roles/storage.admin`)

Click **Continue** → **Done**

### Step 4: Create and Download JSON Key

1. You'll now see your new service account in the list
2. Click on the service account name
3. Navigate to the **Keys** tab
4. Click **Add Key** → **Create New Key**
5. Select **JSON** as the key type
6. Click **Create**

![Google Cloud - Create JSON Key](https://y2i6auvwuaxlu05f.public.blob.vercel-storage.com/google_image3.png)

The JSON key file will automatically download to your computer. This file contains your service account credentials.

### JSON Key File Format

Your downloaded JSON key will look like this:

```json
{
  "type": "service_account",
  "project_id": "your-project-id",
  "private_key_id": "abc123...",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
  "client_email": "captain-storage-access@your-project.iam.gserviceaccount.com",
  "client_id": "123456789",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..."
}
```

### Using Google Cloud Service Accounts with Captain

Store the JSON key file securely (e.g., in a secret management service or as an environment variable)

---

## Azure Blob Storage Setup

### Step 1: Create a Storage Account

1. Log in to the [Azure Portal](https://portal.azure.com/)
2. Navigate to **Storage accounts** → **Create**
3. Select your **Subscription** and **Resource group** (or create a new one)
4. Enter a **Storage account name** (e.g., `captaindocuments`)
5. Select a **Region** closest to your operations
6. Select **Standard** performance and **LRS** (Locally-redundant storage) for general use
7. Click **Review + create** → **Create**

### Step 2: Create a Container

1. Open your new storage account
2. Navigate to **Data storage** → **Containers**
3. Click **+ Container**
4. Enter a container name (e.g., `documents`)
5. Set **Private access level** (no anonymous access)
6. Click **Create**

---

## Azure Blob Storage Credentials

To index files from Azure Blob Storage, you'll need your **Storage Account Name** and **Account Key**.

### Step 1: Get Your Account Name and Key

1. Open your storage account in the [Azure Portal](https://portal.azure.com/)
2. Navigate to **Security + networking** → **Access keys**
3. Your **Storage account name** is displayed at the top
4. Click **Show** next to either key to reveal the **Account Key** (base64-encoded)
5. Copy both values

**Important**: Treat the account key like a password. Store it securely (e.g., in a secret management service or as an environment variable).

### Step 2: Use with Captain

Pass the account name, account key, and container name when calling the indexing endpoint:

#### curl

```bash
curl -X POST https://api.filesearch.dev/v2/collections/my_collection/index/azure \
     -H "Authorization: Bearer $CAPTAIN_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "container_name": "documents",
       "account_name": "captaindocuments",
       "account_key": "your_account_key_base64",
       "processing_type": "advanced"
     }'
```

#### Python

```python
import requests

response = requests.post(
    f"{BASE_URL}/v2/collections/my_collection/index/azure",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "container_name": "documents",
        "account_name": "captaindocuments",
        "account_key": "your_account_key_base64",
        "processing_type": "advanced"  # Required: "advanced", "basic" or "auto"
    }
)

job_id = response.json()['job_id']
print(f"Indexing started! Job ID: {job_id}")
```

#### TypeScript

```typescript
const response = await fetch(
  `${BASE_URL}/v2/collections/my_collection/index/azure`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      container_name: "documents",
      account_name: "captaindocuments",
      account_key: "your_account_key_base64",
      processing_type: "advanced"  // Required: "advanced", "basic" or "auto"
    })
  }
);

const result = await response.json();
console.log(`Indexing started! Job ID: ${result.job_id}`);
```

### Required Azure Permissions

The account key provides full access to all containers in the storage account. For more granular control, you can use **Shared Access Signatures (SAS)** with the following minimum permissions:

* **Read**. to access blob contents
* **List**. to enumerate blobs in the container

You can generate a SAS token from the Azure Portal under your storage account's **Shared access signature** settings.

---

## Cloudflare R2 Setup

### Step 1: Create an R2 Bucket

1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/)
2. Navigate to **R2 Object Storage** → **Create bucket**
3. Enter a bucket name (e.g., `captain-documents`)
4. Select a location hint (optional. R2 automatically distributes globally)
5. Click **Create bucket**

---

## Cloudflare R2 Credentials

To index files from Cloudflare R2, you'll need your **Account ID**, an **Access Key ID**, and a **Secret Access Key**.

### Step 1: Find Your Account ID

1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/)
2. Your **Account ID** is visible in the URL: `https://dash.cloudflare.com/<account_id>`
3. You can also find it on the **R2 Overview** page in the right sidebar

### Step 2: Create an R2 API Token

1. Navigate to **R2 Object Storage** → **Manage R2 API Tokens**
2. Click **Create API token**
3. Enter a token name (e.g., `captain-read-access`)
4. Under **Permissions**, select **Object Read only**
5. Under **Specify bucket(s)**, select your bucket or allow access to all buckets
6. Click **Create API Token**

### Step 3: Retrieve Your Credentials

1. Your **Access Key ID** and **Secret Access Key** will be displayed
2. **Important**: This is the only time you can view the Secret Access Key
3. Copy both values to a secure location

### Step 4: Use with Captain

Pass the account ID, access key ID, secret access key, and bucket name when calling the indexing endpoint:

#### curl

```bash
curl -X POST https://api.filesearch.dev/v2/collections/my_collection/index/r2 \
     -H "Authorization: Bearer $CAPTAIN_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "bucket_name": "captain-documents",
       "account_id": "your_cloudflare_account_id",
       "access_key_id": "your_r2_access_key_id",
       "secret_access_key": "your_r2_secret_access_key",
       "processing_type": "advanced"
     }'
```

#### Python

```python
import requests

response = requests.post(
    f"{BASE_URL}/v2/collections/my_collection/index/r2",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "bucket_name": "captain-documents",
        "account_id": "your_cloudflare_account_id",
        "access_key_id": "your_r2_access_key_id",
        "secret_access_key": "your_r2_secret_access_key",
        "processing_type": "advanced"  # Required: "advanced", "basic" or "auto"
    }
)

job_id = response.json()['job_id']
print(f"Indexing started! Job ID: {job_id}")
```

#### TypeScript

```typescript
const response = await fetch(
  `${BASE_URL}/v2/collections/my_collection/index/r2`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      bucket_name: "captain-documents",
      account_id: "your_cloudflare_account_id",
      access_key_id: "your_r2_access_key_id",
      secret_access_key: "your_r2_secret_access_key",
      processing_type: "advanced"  // Required: "advanced", "basic" or "auto"
    })
  }
);

const result = await response.json();
console.log(`Indexing started! Job ID: ${result.job_id}`);
```

### R2 Jurisdictions

R2 supports jurisdiction-restricted storage. You can optionally specify a `jurisdiction` parameter:

* **`default`**. Global (no restriction). This is the default.
* **`eu`**. EU-only data residency
* **`fedramp`**. FedRAMP-compliant storage
* **`us`**. US-only data residency

### Required R2 Permissions

Your R2 API token needs the following minimum permissions:

* **Object Read**. to access object contents
* **List**. to enumerate objects in the bucket

For more granular control, create a token scoped to a specific bucket rather than all buckets.

---

## Supabase Setup

Supabase Storage is S3-compatible, so Captain connects to it through its S3 endpoint.

### Step 1: Create a Storage Bucket

1. Log in to the [Supabase Dashboard](https://supabase.com/dashboard) and open your project
2. Navigate to **Storage** → **New bucket**
3. Enter a bucket name (e.g., `documents`)
4. Choose whether the bucket is public or private (private is fine. Captain authenticates with S3 credentials)
5. Click **Create bucket**

---

## Supabase Credentials

To index files from Supabase, you'll need the S3 **endpoint URL**, the **region**, an **Access Key ID**, and a **Secret Access Key**.

### Step 1: Enable the S3 Connection

1. In the Supabase Dashboard, navigate to **Storage** → **S3 Connection**
2. Enable the S3 connection if it isn't already enabled
3. Copy the **Endpoint** (e.g., `https://abcdefgh.storage.supabase.co/storage/v1/s3`) and the **Region** shown on this page

Pass this endpoint as `endpoint_url`. The subdomain is your project ref. for `https://abcdefgh.storage.supabase.co/storage/v1/s3` the project ref is `abcdefgh`, but Captain only needs the full `endpoint_url` copied from **Storage → S3 Connection**.

### Step 2: Create S3 Access Keys

1. On the **S3 Connection** page, click **Create new access key** (under **S3 Access Keys**)
2. Enter a description (e.g., `captain-read-access`)
3. Click **Create access key**

### Step 3: Retrieve Your Credentials

1. Your **Access Key ID** and **Secret Access Key** will be displayed
2. **Important**: This is the only time you can view the Secret Access Key
3. Copy both values to a secure location

### Step 4: Use with Captain

Pass the endpoint URL, bucket name, access key ID, and secret access key when calling the indexing endpoint. `region` is optional and defaults to `us-east-1`:

#### curl

```bash
curl -X POST https://api.filesearch.dev/v2/collections/my_collection/index/supabase \
     -H "Authorization: Bearer $CAPTAIN_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "bucket_name": "documents",
       "endpoint_url": "https://abcdefgh.storage.supabase.co/storage/v1/s3",
       "access_key_id": "your_supabase_access_key",
       "secret_access_key": "your_supabase_secret_key",
       "region": "us-east-1",
       "processing_type": "advanced"
     }'
```

#### Python

```python
import requests

response = requests.post(
    f"{BASE_URL}/v2/collections/my_collection/index/supabase",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "bucket_name": "documents",
        "endpoint_url": "https://abcdefgh.storage.supabase.co/storage/v1/s3",
        "access_key_id": "your_supabase_access_key",
        "secret_access_key": "your_supabase_secret_key",
        "region": "us-east-1",
        "processing_type": "advanced"  # Required: "advanced", "basic" or "auto"
    }
)

job_id = response.json()['job_id']
print(f"Indexing started! Job ID: {job_id}")
```

#### TypeScript

```typescript
const response = await fetch(
  `${BASE_URL}/v2/collections/my_collection/index/supabase`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      bucket_name: "documents",
      endpoint_url: "https://abcdefgh.storage.supabase.co/storage/v1/s3",
      access_key_id: "your_supabase_access_key",
      secret_access_key: "your_supabase_secret_key",
      region: "us-east-1",
      processing_type: "advanced"  // Required: "advanced", "basic" or "auto"
    })
  }
);

const result = await response.json();
console.log(`Indexing started! Job ID: ${result.job_id}`);
```

### Endpoint URL

Captain connects using the `endpoint_url` you pass:

* **`endpoint_url`**. The full S3-compatible endpoint URL, copied from **Storage → S3 Connection**. It has the form `https://<project_ref>.storage.supabase.co/storage/v1/s3`, where `<project_ref>` is your project's subdomain (e.g. `abcdefgh`).

### Required Supabase Permissions

The S3 access key needs read and list access to the bucket you want to index. Access keys created from **Storage → S3 Connection** carry the necessary permissions by default.

---

## Backblaze B2 Setup

Backblaze B2 is S3-compatible, so Captain connects to it through its S3 endpoint.

### Step 1: Create a Bucket

1. Log in to the [Backblaze B2 dashboard](https://secure.backblaze.com/user_signin.htm) and open **Buckets**
2. Click **Create a Bucket**
3. Enter a bucket name (e.g., `documents`)
4. Choose whether the bucket is public or private (private is fine. Captain authenticates with S3 credentials)
5. Click **Create a Bucket**
6. Open the bucket's **details** page. it shows the bucket's **Endpoint** (in the form `https://s3.<region>.backblazeb2.com`, e.g. `https://s3.us-west-004.backblazeb2.com`) and its **Region**

---

## Backblaze B2 Credentials

To index files from Backblaze B2, you'll need the S3 **endpoint URL**, the **region**, an **Access Key ID** (Application Key ID), and a **Secret Access Key** (Application Key).

### Step 1: Find the Endpoint and Region

1. In the Backblaze B2 dashboard, open **Buckets** and select your bucket
2. On the bucket's **details** page, copy the **Endpoint** (e.g., `https://s3.us-west-004.backblazeb2.com`) and the **Region** (e.g., `us-west-004`) shown there

Pass this endpoint as `endpoint_url`. The region in the endpoint (e.g. `us-west-004`) must match the `region` you send.

### Step 2: Create an Application Key

1. Navigate to **App Keys** in the Backblaze B2 dashboard
2. Click **Add a New Application Key** (do **not** use the master application key. it cannot be used with the S3-compatible API)
3. Give the key a name and, optionally, scope it to a specific bucket with read access
4. Click **Create New Key**

### Step 3: Retrieve Your Credentials

1. Backblaze displays a **keyID** and an **applicationKey**
2. **Important**: the `applicationKey` is shown only once, at creation time
3. Copy both values to a secure location. use the **keyID** as `access_key_id` and the **applicationKey** as `secret_access_key`

### Step 4: Use with Captain

Pass the endpoint URL, bucket name, access key ID (Application Key ID), secret access key (Application Key), and region when calling the indexing endpoint. Backblaze B2's S3 API requires v4 signatures over HTTPS, which Captain uses by default:

#### curl

```bash
curl -X POST https://api.filesearch.dev/v2/collections/my_collection/index/backblaze \
     -H "Authorization: Bearer $CAPTAIN_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "bucket_name": "documents",
       "endpoint_url": "https://s3.us-west-004.backblazeb2.com",
       "access_key_id": "your_b2_application_key_id",
       "secret_access_key": "your_b2_application_key",
       "region": "us-west-004",
       "processing_type": "advanced"
     }'
```

#### Python

```python
import requests

response = requests.post(
    f"{BASE_URL}/v2/collections/my_collection/index/backblaze",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "bucket_name": "documents",
        "endpoint_url": "https://s3.us-west-004.backblazeb2.com",
        "access_key_id": "your_b2_application_key_id",
        "secret_access_key": "your_b2_application_key",
        "region": "us-west-004",
        "processing_type": "advanced"  # Required: "advanced", "basic" or "auto"
    }
)

job_id = response.json()['job_id']
print(f"Indexing started! Job ID: {job_id}")
```

#### TypeScript

```typescript
const response = await fetch(
  `${BASE_URL}/v2/collections/my_collection/index/backblaze`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      bucket_name: "documents",
      endpoint_url: "https://s3.us-west-004.backblazeb2.com",
      access_key_id: "your_b2_application_key_id",
      secret_access_key: "your_b2_application_key",
      region: "us-west-004",
      processing_type: "advanced"  // Required: "advanced", "basic" or "auto"
    })
  }
);

const result = await response.json();
console.log(`Indexing started! Job ID: ${result.job_id}`);
```

### Endpoint URL

Captain connects using the `endpoint_url` you pass:

* **`endpoint_url`**. The full S3-compatible endpoint URL, copied from the bucket's **details** page. It has the form `https://s3.<region>.backblazeb2.com`, where `<region>` is your bucket's region (e.g. `us-west-004`).

### Required Backblaze B2 Permissions

The Application Key needs read (and list) access to the bucket you want to index. Create the key on the **App Keys** page. the master application key cannot be used with the S3-compatible API.

---

## Dropbox Setup

### Step 1: Create a Dropbox App

1. Log in to the [Dropbox App Console](https://www.dropbox.com/developers/apps)
2. Click **Create app**
3. Under **Choose an API**, select **Scoped access**
4. Under **Choose the type of access you need**, select **Full Dropbox** (or **App folder** to scope to a single folder)
5. Enter an app name (e.g., `captain-indexing`)
6. Click **Create app**

---

## Dropbox Credentials

To index files from Dropbox, you'll need a **Dropbox access token**.

### Step 1: Grant the Required Scopes

1. Open your app in the [Dropbox App Console](https://www.dropbox.com/developers/apps)
2. Navigate to the **Permissions** tab
3. Enable the following scopes:
   * **`files.metadata.read`**. to list files and read their metadata
   * **`files.content.read`**. to download file contents for indexing
   * **`sharing.write`** *(optional)*. lets Captain store a permanent Dropbox link as each document's `source.uri`, so results link straight back to the original file. Without it, indexing still works and the `source.uri` falls back to the file's Dropbox path.
4. Click **Submit** to save the permission changes

### Step 2: Generate an Access Token

1. Navigate to the **Settings** tab
2. Under **OAuth 2**, find **Generated access token**
3. Click **Generate**
4. Copy the token to a secure location

> **Note**
>
> Tokens generated from the App Console are scoped to your own Dropbox account and are short-lived. This is ideal for one-shot indexing jobs, which complete in minutes. Regenerate a token whenever you start a new job.

### Step 3: Use with Captain

Pass the access token when calling the indexing endpoint. The `/index/dropbox` endpoint indexes the **entire account** recursively. To scope a job to a single folder or file, use `/index/dropbox/directory` (with `directory_path`) or `/index/dropbox/file` (with `file_path`) instead:

#### curl

```bash
curl -X POST https://api.filesearch.dev/v2/collections/my_collection/index/dropbox \
     -H "Authorization: Bearer $CAPTAIN_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "dropbox_access_token": "your_dropbox_access_token",
       "processing_type": "advanced"
     }'
```

#### Python

```python
import requests

response = requests.post(
    f"{BASE_URL}/v2/collections/my_collection/index/dropbox",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "dropbox_access_token": "your_dropbox_access_token",
        "processing_type": "advanced"  # Required: "advanced", "basic" or "auto"
    }
)

job_id = response.json()['job_id']
print(f"Indexing started! Job ID: {job_id}")
```

#### TypeScript

```typescript
const response = await fetch(
  `${BASE_URL}/v2/collections/my_collection/index/dropbox`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      dropbox_access_token: "your_dropbox_access_token",
      processing_type: "advanced"  // Required: "advanced", "basic" or "auto"
    })
  }
);

const result = await response.json();
console.log(`Indexing started! Job ID: ${result.job_id}`);
```

### Required Dropbox Scopes

Your Dropbox access token needs the following minimum scopes:

* **`files.metadata.read`**. to list files and read their metadata
* **`files.content.read`**. to download object contents for indexing

Optionally, add **`sharing.write`** so Captain can store a permanent, clickable Dropbox link as each indexed document's `source.uri`. Without it, indexing works normally and `source.uri` falls back to the file's Dropbox path.

Enable these on the **Permissions** tab of the App Console, then regenerate the access token so the new scopes take effect.

---

## Google Drive (headless) Setup

Unlike the other providers, Google Drive uses **service-account domain-wide
delegation** — no per-user OAuth pop-up. Your Google Workspace admin
authorizes one service account once, and Captain reads Drive **as a specific
user** you name on each request: a single file, a folder (recursively), or
that user's entire Drive. Use this to index Drive content at scale, on a
schedule, or on behalf of many users, with no login step in the loop.

> **Info**
>
> The setup below is done once by someone with **Google Workspace super
> administrator** rights on your domain (about 15 minutes). After that,
> indexing is a plain API call.

### Step 1: Create a service account and key

You need a Google Cloud project to hold the service account. **If your
organization doesn't use Google Cloud, that's fine** — any Google account can
create a project for free, and this use case requires **no billing**.

1. Go to [console.cloud.google.com](https://console.cloud.google.com).
2. Create a project (or pick an existing one): open the project picker in the
   top bar, click **New Project**, name it (for example
   `captain-drive-indexing`), and click **Create**.
3. Enable the Drive API: go to **APIs & Services → Enable APIs and services**,
   search for **"Google Drive API"**, and click **Enable**.
4. Create the service account: go to **IAM & Admin → Service Accounts**, click
   **Create service account**, give it a name (for example
   `captain-indexing`), and click **Create and continue**. Skip the optional
   roles and click **Done**. No project roles are needed; this account's
   access comes entirely from the Workspace delegation grant in Step 2.
5. Create a key: open the service account, go to the **Keys** tab, and click
   **Add key → Create new key → JSON → Create**. A `.json` file downloads.
   **This is the `service_account_json` you'll send to Captain — keep it
   secret.**
6. Note the service account's **numeric Client ID** (also called the "Unique
   ID" or "OAuth2 Client ID") from the **Details** tab. You'll need it in
   Step 2.

> **Warning**
>
> Google does **not** offer a way to create a service account entirely inside
> the Admin console — the account itself must be created in Google Cloud, as
> above. Only the **delegation grant** (Step 2) lives in the Admin console.

### Step 2: Authorize the service account (domain-wide delegation)

This is the grant that lets Captain read Drive as your users. It's done in the
**Admin console** (`admin.google.com`), a different console from Step 1.

1. Go to [admin.google.com](https://admin.google.com), then **Security →
   Access and data control → API controls**.
2. In the **Domain-wide delegation** panel, click **Manage Domain-Wide
   Delegation**.
3. Click **Add new** and enter:
   * **Client ID**: the **numeric Client ID** from Step 1 (not the service
     account's email address).
   * **OAuth scopes**: exactly `https://www.googleapis.com/auth/drive.readonly`
4. Click **Authorize**.

> **Warning**
>
> Grant only `drive.readonly` — Captain never writes to or deletes from Drive.
> The grant can take a few minutes to propagate (occasionally up to \~30); if
> your first call returns `unauthorized_client`, wait and retry.

## Google Drive Credentials

Every request sends the service-account key plus a `subject_email` — the
Workspace user whose Drive Captain reads. Only what that user can see is
indexed.

**Choosing `subject_email`:**

* **A specific person's Drive** — their email (e.g. `analyst@yourco.com`).
* **A Shared Drive / team content** — any user who is a **member** of that
  Shared Drive. Files that user can't see are skipped.
* **An org-wide identity** — many teams create a dedicated Workspace user
  (e.g. `indexing@yourco.com`), share the relevant Drives with it, and always
  impersonate that account, keeping the indexed scope explicit and auditable.

Index a single **file**, a **folder** (recursively), or the user's **entire
Drive**:

**`curl`**

```bash title="curl"
# One folder, recursively
curl -X POST "https://api.filesearch.dev/v2/collections/{collection_name}/index/gdrive/directory" \
  -H "Authorization: Bearer $CAPTAIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "service_account_json": "<full contents of the JSON key file, as a string>",
    "subject_email": "analyst@yourco.com",
    "folder_id": "1MHp...",
    "processing_type": "advanced"
  }'
```

Swap the endpoint and id field for other scopes:

* **Single file** — `.../index/gdrive/file` with `"file_id": "1AbC..."` (the
  `/d/<id>/` part of a file URL).
* **Entire Drive** — `.../index/gdrive` with no id field.

`folder_id` is the trailing segment of a folder URL
(`drive.google.com/drive/folders/<folder_id>`).

**What gets indexed:** regular files (PDF, DOCX, XLSX, images, etc.) normally;
native Google **Docs and Slides export to PDF** and **Sheets export to XLSX**
automatically; Shared Drives are included. Google Forms/Sites are skipped, as
is any file the `subject_email` user can't access (logged and skipped — the
job continues).

---

## Troubleshooting

### AWS Issues

**Error: "Invalid AWS credentials"**

* Verify your Access Key ID and Secret Access Key are correct
* Check that the access key is active in the IAM console
* Ensure your IAM user/role has the necessary S3 permissions

**Error: "Access Denied"**

* Verify your IAM permissions include `s3:GetObject` and `s3:ListBucket`
* Check bucket policies and ensure they allow your IAM user/role
* Verify the bucket region matches the `bucket_region` parameter

### Google Cloud Issues

**Error: "Invalid service account credentials"**

* Verify the JSON key file is valid and not corrupted
* Check that the service account is enabled
* Ensure the service account has the necessary Storage permissions

**Error: "Permission denied"**

* Verify the service account has the appropriate Storage role
* Check that the bucket exists and the service account has access
* Review IAM permissions in the Google Cloud Console

### Azure Issues

**Error: "Invalid credentials"**

* Verify the account name matches your storage account exactly
* Check that the account key is the full base64-encoded key (not truncated)
* Ensure the storage account exists and is active

**Error: "Container not found"**

* Verify the container name matches exactly (case-sensitive)
* Check that the container exists in the storage account
* Ensure the storage account is in the correct subscription

### Cloudflare R2 Issues

**Error: "Invalid credentials"**

* Verify your Account ID matches the one in your Cloudflare dashboard URL
* Check that the Access Key ID and Secret Access Key are from an R2 API token (not a general Cloudflare API token)
* Ensure the API token has not been revoked

**Error: "Access Denied"**

* Verify the R2 API token has **Object Read** permissions
* Check that the token is scoped to the correct bucket (or all buckets)
* Ensure the bucket exists and is in the correct jurisdiction

### Supabase Issues

**Error: "Invalid credentials"**

* Verify your `endpoint_url` points at `https://<project_ref>.storage.supabase.co/storage/v1/s3` (copied from **Storage → S3 Connection**)
* Check that the Access Key ID and Secret Access Key are S3 access keys from **Storage → S3 Connection** (not the project's anon/service_role API keys)
* Ensure the S3 connection is enabled for your project

**Error: "Access Denied"**

* Confirm the S3 access key has read and list access to the target bucket
* Check that the `region` matches the one shown on the **S3 Connection** page (defaults to `us-east-1`)
* Ensure the bucket exists and the name is spelled correctly

### Backblaze B2 Issues

**Error: "Invalid credentials"**

* Verify you are using an **Application Key** (created on the **App Keys** page), not the **master application key**. the master key cannot be used with the S3-compatible API
* Check that the **keyID** is passed as `access_key_id` and the **applicationKey** as `secret_access_key`
* Ensure the Application Key has not been deleted

**Error: "Access Denied"**

* Confirm the Application Key has read (and list) access to the target bucket
* Check that the `endpoint_url` and `region` match the values shown on the bucket's **details** page (the region in `https://s3.<region>.backblazeb2.com` must match `region`)
* Ensure the bucket exists and the name is spelled correctly

### Dropbox Issues

**Error: "Invalid credentials"**

* Verify the access token is correct and copied in full (not truncated)
* Tokens generated from the App Console are short-lived. Regenerate a fresh token if the job was created a while after the token was minted
* Ensure the token belongs to the Dropbox account that holds the files you want to index

**Error: "Access Denied"**

* Verify the app has the **`files.metadata.read`** and **`files.content.read`** scopes enabled on the **Permissions** tab
* Regenerate the access token after changing scopes. tokens minted before a scope change do not carry the new permissions
* Check that the `path` exists and is spelled correctly (Dropbox paths are case-insensitive but must otherwise match)

---

## Need Help?

If you encounter issues obtaining or using your cloud storage credentials, contact Captain support:

* Email: [humans@captain.dev](mailto:humans@captain.dev)