# Cloud Drive

Cloud Drive API APIPedia: Kelola file di Dropbox, OneDrive, Yandex Disk, MEGA, pCloud, S3/R2/Wasabi/MinIO, dan Telegram dari satu API. Panggil dengan satu token API di `https://api.apipedia.id/v1/drive`.

Hubungkan penyimpanan awan di halaman Koneksi: Dropbox, OneDrive, dan Yandex Disk lewat login OAuth; MEGA dan pCloud dengan email + password; S3 dan semua yang kompatibel (AWS, Cloudflare R2, Wasabi, Backblaze B2, MinIO, Supabase Storage) dengan access key; atau bot Telegram sebagai gudang berkas. Lalu jelajahi folder, unggah (maks 50 MB), unduh, buat folder, ganti nama, hapus, cek kuota, dan cari berkas di semua akun sekaligus dengan path yang sama untuk semua penyedia. Kredensial disimpan terenkripsi AES-256-GCM.

- Base URL: `https://api.apipedia.id/v1/drive`
- Autentikasi: `Authorization: Bearer apk_…` (token dari console.apipedia.id)
- Kategori: Produktivitas
- Halaman: https://apipedia.id/docs/cloud-drive
- OpenAPI: https://console.apipedia.id/openapi/cloud-drive.yaml

Semua respons memakai amplop `{ "is_success", "message", "data" }` kecuali disebutkan lain. Kode error umum: lihat https://apipedia.id/docs#kode-status-error.

## Endpoint (12)

- `GET /accounts`: Daftar akun drive terhubung
- `POST /accounts`: Hubungkan akun penyimpanan (form kredensial)
- `GET /download`: Unduh berkas
- `GET /files`: Isi folder
- `DELETE /files`: Hapus berkas atau folder
- `POST /folders`: Buat folder
- `POST /oauth/{provider}/start`: Mulai login OAuth Dropbox/OneDrive/Yandex Disk
- `GET /providers`: Daftar penyedia dan field kredensial
- `GET /quota`: Kuota penyimpanan
- `POST /rename`: Ganti nama berkas atau folder
- `GET /search`: Cari berkas di semua akun
- `POST /upload`: Unggah berkas

### Daftar akun drive terhubung

`GET https://api.apipedia.id/v1/drive/accounts`

Semua akun penyimpanan aktif milik Anda dari ketujuh penyedia, default dulu. Kredensial tidak pernah dikembalikan; `meta` hanya data publik (bucket, host, email, region). Pakai `id` sebagai `connection_id` di endpoint lain.

Contoh request:

```bash
curl -X GET "https://api.apipedia.id/v1/drive/accounts" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx"
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Daftar akun drive berhasil diambil",
  "data": {
    "items": [
      {
        "id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
        "toolkit": "drive-s3",
        "accountLabel": "arsip-toko (abc123.r2.cloudflarestorage.com)",
        "isDefault": true,
        "connectedAt": "2026-11-10T02:14:09.512Z",
        "meta": {
          "provider": "s3",
          "bucket": "arsip-toko",
          "host": "abc123.r2.cloudflarestorage.com",
          "prefix": null
        },
        "lastUsedAt": "2026-11-12T09:02:44.118Z"
      },
      {
        "id": "8d41e0b6-27c3-4f5a-b9e2-6a0c1d3e4f57",
        "toolkit": "drive-dropbox",
        "accountLabel": "budi@tokosejahtera.co.id",
        "isDefault": false,
        "connectedAt": "2026-11-11T05:30:00.000Z",
        "lastUsedAt": null,
        "meta": {
          "provider": "dropbox",
          "email": "budi@tokosejahtera.co.id",
          "name": "Budi Santoso"
        }
      }
    ]
  }
}
```

Error khusus endpoint ini:

- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Hubungkan akun penyimpanan (form kredensial)

`POST https://api.apipedia.id/v1/drive/accounts`

Menghubungkan akun S3/kompatibel S3, MEGA, pCloud, atau bot Telegram. `provider` wajib (`s3`, `mega`, `pcloud`, `telegram`; boleh juga bentuk toolkit `drive-s3`); field lain sesuai GET /drive/providers. Kredensial DIUJI lebih dulu (S3: HeadBucket; MEGA/pCloud: login; Telegram: getMe + getChat) dan baru disimpan terenkripsi bila berhasil. Akun pertama otomatis jadi default. `label` opsional (bawaan: `<bucket> (<host>)`, email, atau nama chat).

- S3: `access_key_id`, `secret_access_key`, `bucket` wajib; `region` (bawaan `auto`), `endpoint` (https, kosongkan untuk AWS; host privat ditolak), `prefix`, `force_path_style` (MinIO/Supabase).
- MEGA: `email`, `password`. pCloud: `username` (email), `password` (server AS/EU dipilih otomatis).
- Telegram: `bot_token` dari @BotFather dan `chat_id` (angka -100... atau @namachannel); bot harus anggota chat itu.

Dropbox, OneDrive, dan Yandex Disk ditolak di sini (400) - pakai POST /drive/oauth/{provider}/start.

Error: 400 provider tidak dikenal, field wajib kosong, endpoint bukan https/host privat, bucket tidak bisa diakses, token/chat Telegram tidak valid; 401 login MEGA/pCloud gagal.

Body (`application/json`, wajib):

```json
{
  "provider": "s3",
  "access_key_id": "2f6c1b8e4a9d0c37e5b1f2a8d4c6e9b0",
  "secret_access_key": "8b2e4f6a1c3d5e7f9a0b2c4d6e8f0a1b3c5d7e9f1a2b3c4d5e6f7a8b9c0d1e2f",
  "bucket": "arsip-toko",
  "region": "auto",
  "endpoint": "https://abc123.r2.cloudflarestorage.com"
}
```

Contoh request:

```bash
curl -X POST "https://api.apipedia.id/v1/drive/accounts" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"provider":"s3","access_key_id":"2f6c1b8e4a9d0c37e5b1f2a8d4c6e9b0","secret_access_key":"8b2e4f6a1c3d5e7f9a0b2c4d6e8f0a1b3c5d7e9f1a2b3c4d5e6f7a8b9c0d1e2f","bucket":"arsip-toko","region":"auto","endpoint":"https://abc123.r2.cloudflarestorage.com"}'
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "S3 / kompatibel S3 berhasil dihubungkan",
  "data": {
    "connection": {
      "id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
      "toolkit": "drive-s3",
      "accountLabel": "arsip-toko (abc123.r2.cloudflarestorage.com)",
      "isDefault": true,
      "connectedAt": "2026-11-10T02:14:09.512Z",
      "meta": {
        "provider": "s3",
        "bucket": "arsip-toko",
        "host": "abc123.r2.cloudflarestorage.com",
        "prefix": null
      }
    }
  }
}
```

Error khusus endpoint ini:

- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Unduh berkas

`GET https://api.apipedia.id/v1/drive/download`

Mengunduh satu berkas. Bawaannya jawaban berupa berkas mentah (stream) dengan `Content-Type` berkas dan `Content-Disposition: attachment`. Dengan `format=base64` jawabannya amplop JSON berisi `content_base64` - untuk klien yang hanya bisa membaca JSON, maks 10 MB. Bot Telegram hanya bisa mengunduh berkas sampai 20 MB; folder (MEGA) tidak bisa diunduh langsung. Path selalu berbentuk `/folder/sub/berkas.ext` yang sama untuk semua penyedia (diawali `/`, tanpa `..`, tiap bagian maks 255 karakter).

Akun dipilih lewat `connection_id` (query, atau body untuk POST/PATCH). Tanpa itu dipakai akun default, atau satu-satunya akun Drive. Error pemilihan akun: 400 connection_id bukan uuid; 404 connection_id tidak ditemukan/sudah diputus; 409 beberapa akun tanpa default (`data.accounts` berisi pilihan); 412 belum ada akun terhubung.

Error lain: 400 path kosong/folder; 404 berkas tidak ditemukan; 413 format=base64 untuk berkas >10 MB atau berkas Telegram >20 MB.

| Parameter | Lokasi | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
| `path` | query | string | ya | Path berkas yang diunduh. |
| `format` | query | string | tidak | `base64`: amplop JSON berisi isi berkas (maks 10 MB). Kosongkan untuk berkas mentah. Pilihan: `base64`. |
| `connection_id` | query | string | tidak | Id koneksi drive (uuid, dari GET /accounts produk ini atau GET /connections). Kosongkan untuk memakai akun default, atau satu-satunya akun. |

Contoh request:

```bash
curl -X GET "https://api.apipedia.id/v1/drive/download?path=%2Flaporan%2F2026%2Ffaktur-november.pdf&connection_id=3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx"
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Berkas berhasil diunduh",
  "data": {
    "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
    "path": "/catatan/todo.txt",
    "name": "todo.txt",
    "size": 27,
    "mime_type": "text/plain",
    "content_base64": "LSBLaXJpbSBmYWt0dXIgSU5WLTExODcK"
  }
}
```

Error khusus endpoint ini:

- **409**: Ada beberapa akun Drive tanpa default dan connection_id tidak diisi. data.accounts berisi pilihan id. (`Anda punya 2 akun Drive tanpa default. Sebutkan connection_id, atau tandai satu akun sebagai default di halaman Koneksi.`)
- **412**: Belum ada akun Drive yang terhubung. Hubungkan dulu di halaman Koneksi console atau lewat POST /accounts. (`Belum ada akun Drive yang terhubung. Hubungkan dulu di console.apipedia.id/connections.`)
- **413**: format=base64 untuk berkas lebih dari 10 MB, atau berkas Telegram lebih dari 20 MB (`format=base64 hanya untuk berkas sampai 10 MB. Unduh tanpa format untuk berkas lebih besar.`)
- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Isi folder

`GET https://api.apipedia.id/v1/drive/files`

Daftar berkas dan subfolder di satu folder, folder dulu lalu urut nama. Path selalu berbentuk `/folder/sub/berkas.ext` yang sama untuk semua penyedia (diawali `/`, tanpa `..`, tiap bagian maks 255 karakter). Bawaan `path` adalah akar `/`. Setiap item: `name`, `path`, `is_folder`, `size` (byte; 0 untuk folder), `mime_type` (null untuk folder; ditebak dari ekstensi bila penyedia tidak memberi), `modified_at` (ISO 8601 atau null). Maks sekitar 5.000 item per folder.

Akun dipilih lewat `connection_id` (query, atau body untuk POST/PATCH). Tanpa itu dipakai akun default, atau satu-satunya akun Drive. Error pemilihan akun: 400 connection_id bukan uuid; 404 connection_id tidak ditemukan/sudah diputus; 409 beberapa akun tanpa default (`data.accounts` berisi pilihan); 412 belum ada akun terhubung.

Prasyarat: akun penyimpanan harus terhubung lebih dulu - di halaman Koneksi console (console.apipedia.id/connections, kartu Cloud Drive per penyedia), lewat POST /drive/accounts (S3, MEGA, pCloud, Telegram), atau POST /drive/oauth/{provider}/start (Dropbox, OneDrive, Yandex Disk). Kredensial disimpan terenkripsi AES-256-GCM.

Error lain: 400 path mengandung ".." atau path berkas (MEGA); 404 path tidak ditemukan; 401/502 penyedia menolak/gagal.

| Parameter | Lokasi | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
| `path` | query | string | tidak | Folder yang dibuka, mis. `/laporan/2026`. Default `/`. |
| `connection_id` | query | string | tidak | Id koneksi drive (uuid, dari GET /accounts produk ini atau GET /connections). Kosongkan untuk memakai akun default, atau satu-satunya akun. |

Contoh request:

```bash
curl -X GET "https://api.apipedia.id/v1/drive/files?path=%2Flaporan%2F2026&connection_id=3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx"
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Isi folder berhasil diambil",
  "data": {
    "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
    "provider": "s3",
    "path": "/laporan/2026",
    "items": [
      {
        "name": "foto-produk",
        "path": "/laporan/2026/foto-produk",
        "is_folder": true,
        "size": 0,
        "mime_type": null,
        "modified_at": null
      },
      {
        "name": "faktur-november.pdf",
        "path": "/laporan/2026/faktur-november.pdf",
        "is_folder": false,
        "size": 248113,
        "mime_type": "application/pdf",
        "modified_at": "2026-11-12T03:15:42.000Z"
      },
      {
        "name": "rekap-penjualan.xlsx",
        "path": "/laporan/2026/rekap-penjualan.xlsx",
        "is_folder": false,
        "size": 53120,
        "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "modified_at": "2026-11-10T07:22:05.000Z"
      }
    ]
  }
}
```

Error khusus endpoint ini:

- **409**: Ada beberapa akun Drive tanpa default dan connection_id tidak diisi. data.accounts berisi pilihan id. (`Anda punya 2 akun Drive tanpa default. Sebutkan connection_id, atau tandai satu akun sebagai default di halaman Koneksi.`)
- **412**: Belum ada akun Drive yang terhubung. Hubungkan dulu di halaman Koneksi console atau lewat POST /accounts. (`Belum ada akun Drive yang terhubung. Hubungkan dulu di console.apipedia.id/connections.`)
- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Hapus berkas atau folder

`DELETE https://api.apipedia.id/v1/drive/files`

Menghapus berkas, atau folder beserta seluruh isinya. Path selalu berbentuk `/folder/sub/berkas.ext` yang sama untuk semua penyedia (diawali `/`, tanpa `..`, tiap bagian maks 255 karakter). Folder akar `/` tidak bisa dihapus. Perilaku per penyedia: MEGA ke Rubbish Bin, OneDrive ke Recycle bin, Yandex Disk ke Trash, Dropbox mengikuti riwayat berkas Dropbox; S3, pCloud, dan Telegram menghapus permanen (Telegram juga menghapus pesannya). S3 menolak folder berisi lebih dari 10.000 objek (413).

Akun dipilih lewat `connection_id` (query, atau body untuk POST/PATCH). Tanpa itu dipakai akun default, atau satu-satunya akun Drive. Error pemilihan akun: 400 connection_id bukan uuid; 404 connection_id tidak ditemukan/sudah diputus; 409 beberapa akun tanpa default (`data.accounts` berisi pilihan); 412 belum ada akun terhubung.

Error lain: 400 path kosong/akar; 404 path tidak ditemukan.

| Parameter | Lokasi | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
| `path` | query | string | ya | Berkas atau folder yang dihapus. |
| `connection_id` | query | string | tidak | Id koneksi drive (uuid, dari GET /accounts produk ini atau GET /connections). Kosongkan untuk memakai akun default, atau satu-satunya akun. |

Contoh request:

```bash
curl -X DELETE "https://api.apipedia.id/v1/drive/files?path=%2Flaporan%2F2026%2Ffaktur-november.pdf&connection_id=3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx"
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Berhasil dihapus",
  "data": {
    "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
    "path": "/laporan/2026/faktur-november.pdf"
  }
}
```

Error khusus endpoint ini:

- **409**: Ada beberapa akun Drive tanpa default dan connection_id tidak diisi. data.accounts berisi pilihan id. (`Anda punya 2 akun Drive tanpa default. Sebutkan connection_id, atau tandai satu akun sebagai default di halaman Koneksi.`)
- **412**: Belum ada akun Drive yang terhubung. Hubungkan dulu di halaman Koneksi console atau lewat POST /accounts. (`Belum ada akun Drive yang terhubung. Hubungkan dulu di console.apipedia.id/connections.`)
- **413**: Folder S3 berisi lebih dari 10.000 objek (`Folder terlalu besar untuk operasi ini (lebih dari 10.000 objek).`)
- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Buat folder

`POST https://api.apipedia.id/v1/drive/folders`

Membuat folder baru di `path` (path lengkap folder baru, mis. `/laporan/2026`). Di S3 folder adalah objek penanda kosong berakhiran "/"; di Telegram folder induk ikut dibuat; di pCloud folder yang sudah ada tidak dianggap galat; di Dropbox dan OneDrive folder bernama sama menghasilkan 409 dari penyedia. Path selalu berbentuk `/folder/sub/berkas.ext` yang sama untuk semua penyedia (diawali `/`, tanpa `..`, tiap bagian maks 255 karakter).

Akun dipilih lewat `connection_id` (query, atau body untuk POST/PATCH). Tanpa itu dipakai akun default, atau satu-satunya akun Drive. Error pemilihan akun: 400 connection_id bukan uuid; 404 connection_id tidak ditemukan/sudah diputus; 409 beberapa akun tanpa default (`data.accounts` berisi pilihan); 412 belum ada akun terhubung.

Error lain: 400 path kosong/akar atau nama tidak valid; 404 folder induk tidak ditemukan (MEGA).

Body (`application/json`, wajib):

```json
{
  "path": "/laporan/2026",
  "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d"
}
```

Contoh request:

```bash
curl -X POST "https://api.apipedia.id/v1/drive/folders" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"path":"/laporan/2026","connection_id":"3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d"}'
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Folder berhasil dibuat",
  "data": {
    "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
    "item": {
      "name": "2026",
      "path": "/laporan/2026",
      "is_folder": true,
      "size": 0,
      "mime_type": null,
      "modified_at": null
    }
  }
}
```

Error khusus endpoint ini:

- **409**: Ada beberapa akun Drive tanpa default dan connection_id tidak diisi. data.accounts berisi pilihan id. (`Anda punya 2 akun Drive tanpa default. Sebutkan connection_id, atau tandai satu akun sebagai default di halaman Koneksi.`)
- **412**: Belum ada akun Drive yang terhubung. Hubungkan dulu di halaman Koneksi console atau lewat POST /accounts. (`Belum ada akun Drive yang terhubung. Hubungkan dulu di console.apipedia.id/connections.`)
- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Mulai login OAuth Dropbox/OneDrive/Yandex Disk

`POST https://api.apipedia.id/v1/drive/oauth/{provider}/start`

Mengembalikan `redirectUrl` halaman izin penyedia. Buka di browser pengguna; setelah izin diberikan penyedia memanggil callback APIPedia, akun disimpan, lalu browser diarahkan ke halaman Koneksi console (`/connections?toolkit=drive-<provider>&status=active` atau `status=failed&reason=...`). Tautan berlaku 15 menit (state bertanda tangan HMAC). Cek hasilnya dengan GET /drive/accounts. Token akses disegarkan otomatis memakai refresh token.

Body opsional: `label` (nama akun di console; bawaan email/login penyedia) dan `app_origin` (origin console tujuan kembali; hanya console.apipedia.id atau localhost yang diterima).

Error: 400 provider tidak dikenal atau bukan penyedia OAuth; 503 login penyedia ini belum diaktifkan di server (client id/secret kosong) atau kunci enkripsi belum diatur.

| Parameter | Lokasi | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
| `provider` | path | string | ya | Penyedia OAuth: `dropbox`, `onedrive`, atau `yandex`. Pilihan: `dropbox`, `onedrive`, `yandex`. |

Body (`application/json`):

```json
{
  "label": "Dropbox kantor"
}
```

Contoh request:

```bash
curl -X POST "https://api.apipedia.id/v1/drive/oauth/dropbox/start" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"label":"Dropbox kantor"}'
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Buka redirectUrl untuk login",
  "data": {
    "redirectUrl": "https://www.dropbox.com/oauth2/authorize?client_id=k3x9q2m7v1p8w4z&response_type=code&redirect_uri=https%3A%2F%2Fapi.apipedia.id%2Fv1%2Fdrive-oauth%2Fdropbox%2Fcallback&state=eyJ1Ijoi...&token_access_type=offline"
  }
}
```

Error khusus endpoint ini:

- **503**: Login penyedia belum diaktifkan di server, atau kunci enkripsi belum diatur (`Login Dropbox belum diaktifkan di server (DROPBOX_APP_KEY/DROPBOX_APP_SECRET kosong). Hubungi admin APIPedia.`)

### Daftar penyedia dan field kredensial

`GET https://api.apipedia.id/v1/drive/providers`

Tujuh penyedia yang didukung beserta field kredensial untuk POST /drive/accounts. `oauth: true` (Dropbox, OneDrive, Yandex Disk) berarti dihubungkan lewat login di browser (POST /drive/oauth/{provider}/start), bukan form. `provider` adalah nilai yang dikirim ke field `provider`; `toolkit` adalah nama koneksi di GET /connections. Google Drive tidak termasuk di sini - pakai produk Google Drive (koneksi OAuth `googledrive`).

Contoh request:

```bash
curl -X GET "https://api.apipedia.id/v1/drive/providers" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx"
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Daftar penyedia drive",
  "data": {
    "items": [
      {
        "toolkit": "drive-s3",
        "provider": "s3",
        "name": "S3 / kompatibel S3",
        "description": "AWS S3, Cloudflare R2, Wasabi, Backblaze B2, MinIO, Supabase Storage (S3), IDCloudHost, dll.",
        "fields": [
          {
            "key": "access_key_id",
            "label": "Access key ID",
            "type": "text",
            "required": true
          },
          {
            "key": "secret_access_key",
            "label": "Secret access key",
            "type": "password",
            "required": true
          },
          {
            "key": "bucket",
            "label": "Bucket",
            "type": "text",
            "required": true
          },
          {
            "key": "region",
            "label": "Region",
            "type": "text",
            "placeholder": "auto / us-east-1 / ap-southeast-1"
          },
          {
            "key": "endpoint",
            "label": "Endpoint (kosongkan untuk AWS)",
            "type": "url",
            "placeholder": "https://<akun>.r2.cloudflarestorage.com"
          },
          {
            "key": "prefix",
            "label": "Folder dasar (opsional)",
            "type": "text",
            "placeholder": "mis. backup/"
          },
          {
            "key": "force_path_style",
            "label": "Path-style URL (MinIO/Supabase)",
            "type": "boolean"
          }
        ],
        "oauth": false
      },
      {
        "toolkit": "drive-mega",
        "provider": "mega",
        "name": "MEGA",
        "description": "Penyimpanan terenkripsi end-to-end, 20 GB gratis.",
        "fields": [
          {
            "key": "email",
            "label": "Email MEGA",
            "type": "email",
            "required": true
          },
          {
            "key": "password",
            "label": "Password MEGA",
            "type": "password",
            "required": true
          }
        ],
        "oauth": false
      },
      {
        "toolkit": "drive-pcloud",
        "provider": "pcloud",
        "name": "pCloud",
        "description": "10 GB gratis, server di AS atau Eropa.",
        "fields": [
          {
            "key": "username",
            "label": "Email pCloud",
            "type": "email",
            "required": true
          },
          {
            "key": "password",
            "label": "Password pCloud",
            "type": "password",
            "required": true
          }
        ],
        "oauth": false
      },
      {
        "toolkit": "drive-dropbox",
        "provider": "dropbox",
        "name": "Dropbox",
        "description": "Login lewat akun Dropbox (OAuth). 2 GB gratis.",
        "fields": [],
        "oauth": true
      },
      {
        "toolkit": "drive-onedrive",
        "provider": "onedrive",
        "name": "OneDrive",
        "description": "Login lewat akun Microsoft (OAuth). 5 GB gratis.",
        "fields": [],
        "oauth": true
      },
      {
        "toolkit": "drive-yandex",
        "provider": "yandex",
        "name": "Yandex Disk",
        "description": "Login lewat akun Yandex (OAuth). 10 GB gratis.",
        "fields": [],
        "oauth": true
      },
      {
        "toolkit": "drive-telegram",
        "provider": "telegram",
        "name": "Telegram",
        "description": "Bot Telegram menyimpan berkas di chat/channel pribadimu. Maks 50 MB unggah, 20 MB unduh per berkas.",
        "fields": [
          {
            "key": "bot_token",
            "label": "Token bot (dari @BotFather)",
            "type": "password",
            "required": true
          },
          {
            "key": "chat_id",
            "label": "Chat ID channel/grup penyimpanan",
            "type": "text",
            "required": true,
            "placeholder": "-100xxxxxxxxxx"
          }
        ],
        "oauth": false
      }
    ]
  }
}
```

Error khusus endpoint ini:

- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Kuota penyimpanan

`GET https://api.apipedia.id/v1/drive/quota`

Kapasitas terpakai dan total akun, dalam byte. `quota` null untuk S3 dan yang kompatibel (tidak punya kuota tetap; pesan "Penyedia ini tidak memiliki kuota tetap"). Telegram melaporkan total ukuran berkas yang diunggah lewat APIPedia dengan `total: 0` (tanpa batas).

Akun dipilih lewat `connection_id` (query, atau body untuk POST/PATCH). Tanpa itu dipakai akun default, atau satu-satunya akun Drive. Error pemilihan akun: 400 connection_id bukan uuid; 404 connection_id tidak ditemukan/sudah diputus; 409 beberapa akun tanpa default (`data.accounts` berisi pilihan); 412 belum ada akun terhubung.

| Parameter | Lokasi | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
| `connection_id` | query | string | tidak | Id koneksi drive (uuid, dari GET /accounts produk ini atau GET /connections). Kosongkan untuk memakai akun default, atau satu-satunya akun. |

Contoh request:

```bash
curl -X GET "https://api.apipedia.id/v1/drive/quota?connection_id=3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx"
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Kuota berhasil diambil",
  "data": {
    "connection_id": "8d41e0b6-27c3-4f5a-b9e2-6a0c1d3e4f57",
    "quota": {
      "used": 734003200,
      "total": 2147483648
    }
  }
}
```

Error khusus endpoint ini:

- **409**: Ada beberapa akun Drive tanpa default dan connection_id tidak diisi. data.accounts berisi pilihan id. (`Anda punya 2 akun Drive tanpa default. Sebutkan connection_id, atau tandai satu akun sebagai default di halaman Koneksi.`)
- **412**: Belum ada akun Drive yang terhubung. Hubungkan dulu di halaman Koneksi console atau lewat POST /accounts. (`Belum ada akun Drive yang terhubung. Hubungkan dulu di console.apipedia.id/connections.`)
- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Ganti nama berkas atau folder

`POST https://api.apipedia.id/v1/drive/rename`

Mengganti nama berkas atau folder di `path` menjadi `name` (tetap di folder yang sama; bukan untuk memindahkan). Folder ikut membawa seluruh isinya (di S3 setiap objek disalin lalu yang lama dihapus). Path selalu berbentuk `/folder/sub/berkas.ext` yang sama untuk semua penyedia (diawali `/`, tanpa `..`, tiap bagian maks 255 karakter).

Akun dipilih lewat `connection_id` (query, atau body untuk POST/PATCH). Tanpa itu dipakai akun default, atau satu-satunya akun Drive. Error pemilihan akun: 400 connection_id bukan uuid; 404 connection_id tidak ditemukan/sudah diputus; 409 beberapa akun tanpa default (`data.accounts` berisi pilihan); 412 belum ada akun terhubung.

Error lain: 400 path kosong/akar atau name kosong/mengandung "/"; 404 path tidak ditemukan; 409 nama tujuan sudah ada (Dropbox).

Body (`application/json`, wajib):

```json
{
  "path": "/laporan/2026/faktur-november.pdf",
  "name": "faktur-nov-2026.pdf",
  "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d"
}
```

Contoh request:

```bash
curl -X POST "https://api.apipedia.id/v1/drive/rename" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"path":"/laporan/2026/faktur-november.pdf","name":"faktur-nov-2026.pdf","connection_id":"3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d"}'
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Nama berhasil diganti",
  "data": {
    "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
    "from": "/laporan/2026/faktur-november.pdf",
    "to": "/laporan/2026/faktur-nov-2026.pdf"
  }
}
```

Error khusus endpoint ini:

- **409**: Ada beberapa akun Drive tanpa default dan connection_id tidak diisi. data.accounts berisi pilihan id. (`Anda punya 2 akun Drive tanpa default. Sebutkan connection_id, atau tandai satu akun sebagai default di halaman Koneksi.`)
- **412**: Belum ada akun Drive yang terhubung. Hubungkan dulu di halaman Koneksi console atau lewat POST /accounts. (`Belum ada akun Drive yang terhubung. Hubungkan dulu di console.apipedia.id/connections.`)
- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Cari berkas di semua akun

`GET https://api.apipedia.id/v1/drive/search`

Mencari berkas/folder yang namanya mengandung `q` di SEMUA akun drive Anda sekaligus (maks 20 akun), atau di satu akun bila `connection_id` diisi. Maks 100 hasil per akun, batas waktu 25 detik per akun. Setiap hasil membawa `connection_id`, `provider`, dan `account_label` akunnya. Akun yang gagal TIDAK menggagalkan hasil akun lain: dilaporkan di `errors` (`connection_id`, `status`, `message`; 504 bila terlalu lama). S3 dan Yandex Disk memindai daftar berkas lalu menyaring nama; penyedia lain memakai pencarian bawaannya.

Error: 400 q kurang dari 2 karakter atau connection_id bukan uuid.

| Parameter | Lokasi | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
| `q` | query | string | ya | Kata kunci nama berkas, 2-100 karakter. Alias: `query`. |
| `connection_id` | query | string | tidak | Batasi pencarian ke satu akun drive (uuid). Kosongkan untuk mencari di semua akun. |

Contoh request:

```bash
curl -X GET "https://api.apipedia.id/v1/drive/search?q=faktur" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx"
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Hasil pencarian",
  "data": {
    "q": "faktur",
    "items": [
      {
        "name": "faktur-november.pdf",
        "path": "/laporan/2026/faktur-november.pdf",
        "is_folder": false,
        "size": 248113,
        "mime_type": "application/pdf",
        "modified_at": "2026-11-12T03:15:42.000Z",
        "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
        "provider": "s3",
        "account_label": "arsip-toko (abc123.r2.cloudflarestorage.com)"
      },
      {
        "name": "faktur-oktober.pdf",
        "path": "/Dokumen/faktur-oktober.pdf",
        "is_folder": false,
        "size": 231877,
        "mime_type": "application/pdf",
        "modified_at": "2026-10-31T10:04:19.000Z",
        "connection_id": "8d41e0b6-27c3-4f5a-b9e2-6a0c1d3e4f57",
        "provider": "dropbox",
        "account_label": "budi@tokosejahtera.co.id"
      }
    ],
    "errors": []
  }
}
```

Error khusus endpoint ini:

- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)

### Unggah berkas

`POST https://api.apipedia.id/v1/drive/upload`

Mengunggah satu berkas ke folder `path` dengan nama `filename` (berkas bernama sama DITIMPA). Dua cara mengirim isi:
- Body mentah (disarankan): `Content-Type: application/octet-stream` (atau tipe MIME berkas), `path` dan `filename` di query, maks 50 MB. Contoh: `curl -X POST "https://api.apipedia.id/v1/drive/upload?path=/laporan/2026&filename=faktur.pdf" -H "Authorization: Bearer apk_..." -H "Content-Type: application/pdf" --data-binary @faktur.pdf`
- JSON `{ path, filename, content_base64, connection_id? }` untuk klien yang hanya bisa mengirim JSON (seluruh body JSON maks 12 MB). Prefix data URL (`data:...;base64,`) dibuang otomatis.

Tipe MIME diambil dari header Content-Type bila bukan octet-stream/JSON, selain itu ditebak dari ekstensi `filename`. Batas penyedia: Telegram 50 MB per berkas. Folder induk yang belum ada dibuat otomatis di S3 dan Telegram. Path selalu berbentuk `/folder/sub/berkas.ext` yang sama untuk semua penyedia (diawali `/`, tanpa `..`, tiap bagian maks 255 karakter).

Akun dipilih lewat `connection_id` (query, atau body untuk POST/PATCH). Tanpa itu dipakai akun default, atau satu-satunya akun Drive. Error pemilihan akun: 400 connection_id bukan uuid; 404 connection_id tidak ditemukan/sudah diputus; 409 beberapa akun tanpa default (`data.accounts` berisi pilihan); 412 belum ada akun terhubung.

Error lain: 400 isi kosong atau filename tidak valid (kosong atau mengandung "/"); 413 lebih dari 50 MB.

| Parameter | Lokasi | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
| `path` | query | string | tidak | Folder tujuan. Bawaan `/`. Boleh juga di body JSON. Default `/`. |
| `filename` | query | string | tidak | Nama berkas (wajib, di query atau body JSON), maks 255 karakter, tanpa "/". |
| `connection_id` | query | string | tidak | Id koneksi drive (uuid, dari GET /accounts produk ini atau GET /connections). Kosongkan untuk memakai akun default, atau satu-satunya akun. |

Body (`application/json`, wajib):

```json
{
  "path": "/catatan",
  "filename": "todo.txt",
  "content_base64": "LSBLaXJpbSBmYWt0dXIgSU5WLTExODcK"
}
```

Contoh request:

```bash
curl -X POST "https://api.apipedia.id/v1/drive/upload?path=%2Fcatatan&filename=todo.txt&connection_id=3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d" \
  -H "Authorization: Bearer apk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"path":"/catatan","filename":"todo.txt","content_base64":"LSBLaXJpbSBmYWt0dXIgSU5WLTExODcK"}'
```

Contoh respons (200):

```json
{
  "is_success": true,
  "message": "Berkas berhasil diunggah",
  "data": {
    "connection_id": "3f9c2a71-8b4e-4d2a-9c61-5e7f0a1b2c3d",
    "item": {
      "name": "todo.txt",
      "path": "/catatan/todo.txt",
      "is_folder": false,
      "size": 27,
      "mime_type": "text/plain",
      "modified_at": "2026-11-12T09:15:03.221Z"
    }
  }
}
```

Error khusus endpoint ini:

- **409**: Ada beberapa akun Drive tanpa default dan connection_id tidak diisi. data.accounts berisi pilihan id. (`Anda punya 2 akun Drive tanpa default. Sebutkan connection_id, atau tandai satu akun sebagai default di halaman Koneksi.`)
- **412**: Belum ada akun Drive yang terhubung. Hubungkan dulu di halaman Koneksi console atau lewat POST /accounts. (`Belum ada akun Drive yang terhubung. Hubungkan dulu di console.apipedia.id/connections.`)
- **413**: Body lebih dari 50 MB (mentah) atau 12 MB (JSON) (`request entity too large`)
- **503**: Kunci enkripsi kredensial (CONNECTION_SECRET_KEY) belum diatur di server (`Fitur ini belum diaktifkan di server: CONNECTION_SECRET_KEY belum diatur. Hubungi admin APIPedia.`)
