
# API Keys API

Endpoint ini memungkinkan Anda mengelola kunci API akun Anda dari kode. Semuanya hanya beroperasi pada kunci milik akun pemanggil.

Ada dua jenis kunci, dan keduanya berada di jalur yang terpisah:

- **Kunci utama Anda** — satu-satunya kunci akses penuh di bawah **Pengaturan → Integrasi → Kunci API**. Cari pratinjau tersamar, periksa penggunaan batas kecepatan Anda, rotasi, atau cabut kunci tersebut. Ini adalah endpoint `/api-keys/current`, `/api-keys/rotate`, dan `/api-keys/usage` di bawah ini.
- **Kunci cakupan (Scoped keys)** — kunci tambahan bernama yang Anda buat untuk tugas tertentu, masing-masing dibatasi pada bagian API yang Anda pilih. Ini adalah endpoint `/api-keys` dan `/api-keys/{id}` di bawah [Kunci cakupan](#scoped-keys). Tidak ada perubahan pada kunci utama Anda saat Anda membuat kunci ini; integrasi yang ada tetap tidak terpengaruh.

Semua jalur di bawah ini bersifat relatif terhadap URL dasar API:

```
https://api.youraiconnector.com/v1
```

Setiap permintaan harus diautentikasi. Lihat [Autentikasi](authentication.md) untuk empat metode yang diterima. Contoh di sini menggunakan header `X-API-Key` (dan satu bentuk parameter kueri untuk cURL).

> **Baca ini terlebih dahulu.** Merotasi atau mencabut kunci Anda akan berlaku **seketika**. Saat panggilan berhasil, kunci lama akan berhenti berfungsi — setiap integrasi yang masih menggunakannya akan mulai menerima kesalahan `401`. Rencanakan hal ini: lakukan rotasi selama jendela pemeliharaan dan segera perbarui semua integrasi Anda.

---

## Mendapatkan metadata kunci saat ini

Mengembalikan kunci aktif Anda: kunci lengkap di `api_key` jika salinan yang dapat diambil tersedia, pratinjau yang disamarkan (4 karakter pertama dan 4 karakter terakhir), dan, jika tersedia, tanggal pembuatannya. `api_key` adalah `null` untuk kunci yang dibuat sebelum salinan yang dapat diambil disimpan — lakukan rotasi sekali dan kunci baru tersebut dapat ditampilkan kembali di kemudian hari.

`GET /api-keys/current`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/api-keys/current?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/current", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/api-keys/current",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "api_key": "abcdEFGH1234ijkl5678MNOP9012qrst",
  "api_key_masked": "abcd...qrst",
  "created_at": "2026-06-01T10:00:00.000Z"
}
```

Jika akun tidak memiliki kunci API, responsnya adalah `404` dengan `{ "success": false, "error": "No API key found for this account" }`.

---

## Mendapatkan penggunaan batas laju (rate-limit)

Mengembalikan penggunaan batas laju Anda untuk jendela saat ini: batas permintaan per jendela, berapa banyak permintaan yang telah dihitung sejauh ini, berapa banyak yang tersisa, dan kapan jendela tersebut diatur ulang. Gunakan ini untuk membangun pembatasan (throttling) sisi klien agar integrasi Anda melambat sebelum mencapai respons `429`.

`GET /api-keys/usage`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/api-keys/usage" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/usage", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/api-keys/usage",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "usage": {
    "limit": 300,
    "window_seconds": 60,
    "used": 37,
    "remaining": 263,
    "window_resets_at": "2026-06-09T12:01:00.000Z"
  }
}
```

Jika belum ada permintaan yang tercatat di jendela saat ini, penggunaan akan dilaporkan sebagai nol dan respons menyertakan kolom `note` yang menjelaskan alasannya.

---

## Merotasi kunci

Menghasilkan kunci API baru dan membatalkan kunci sebelumnya dalam satu langkah yang sama. Gunakan ini jika Anda mencurigai kunci Anda telah bocor, atau sebagai bagian dari kebijakan rotasi kredensial rutin.

`POST /api-keys/rotate`

> **Kunci baru hanya ditampilkan sekali.** Kunci ini dikembalikan dalam respons ini dan tidak dapat diambil secara penuh setelahnya — simpan dengan aman segera setelah Anda menerimanya. Kunci sebelumnya akan berhenti berfungsi seketika setelah panggilan ini berhasil, jadi perbarui setiap integrasi yang menggunakannya.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/api-keys/rotate?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/rotate", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
// Save data.api_key now — it will not be shown again.
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/api-keys/rotate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Save data["api_key"] now — it will not be shown again.
```

**Respons**

```json
{
  "success": true,
  "api_key": "abcdEFGH1234ijkl5678MNOP9012qrst",
  "message": "API key rotated. The previous key is no longer valid. Store this key now — it will not be shown again."
}
```

---

## Cabut kunci

Menghapus kunci API akun Anda secara permanen. Pencabutan bersifat langsung: setiap permintaan berikutnya yang menggunakan kunci yang dicabut — termasuk integrasi seperti Make, Zapier, atau skrip kustom — akan ditolak dengan `401`. Untuk memulihkan akses API setelahnya, buat kunci baru dari pengaturan akun Anda saat masuk ke aplikasi.

`DELETE /api-keys/current`

> **Tidak ada opsi batalkan.** Berbeda dengan rotasi, pencabutan tidak memberikan kunci pengganti. Hanya lakukan pencabutan jika Anda bermaksud menghentikan akses API (misalnya, kunci yang bocor dan tidak dapat segera diganti).

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/api-keys/current" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/current", {
  method: "DELETE",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/api-keys/current",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "revoked": true,
  "message": "API key revoked. All requests using it will be rejected immediately."
}
```

Jika akun tidak memiliki kunci untuk dicabut, responsnya adalah `404`.

---

## Kunci cakupan

Kunci cakupan adalah kunci API tambahan yang Anda buat untuk satu tugas tertentu, yang hanya membawa akses yang dibutuhkan tugas tersebut. Contoh klasik: Anda ingin mengarahkan dasbor klien, alat pelaporan, atau skrip internal ke akun Anda tanpa memberikan kunci yang juga dapat mengirim pesan, mengubah agen AI Anda, atau membeli nomor telepon.

Pembatasan tersebut menyertai kunci itu sendiri, sehingga siapa pun yang memegangnya hanya dapat melakukan apa yang Anda izinkan saat Anda membuatnya.

**Apa yang dapat Anda batasi**

| Bidang | Apa artinya |
|---|---|
| `read_only` | `true` (default) berarti hanya permintaan baca yang diizinkan. Setiap pembuatan, pembaruan, atau penghapusan akan ditolak. |
| `tags` | Daftar bagian API yang boleh digunakan kunci, ditulis dengan nama bagian yang sama seperti yang Anda lihat di dokumen ini dan di [API explorer](reference.md) — `Analytics`, `Campaigns`, `Contacts`, `Messages`, `Appointments`, dan seterusnya. Daftar kosong berarti setiap bagian. |
| `sub_account_ids` | Akun terkelola mana yang boleh ditindaklanjuti oleh kunci tersebut. Kosong berarti hanya akun Anda sendiri; `["*"]` berarti akun apa pun yang benar-benar Anda kelola. Kepemilikan tetap diperiksa pada setiap permintaan. |
| `rate_limit_per_min` | Permintaan per menit untuk kunci ini, dihitung dalam anggarannya sendiri sehingga tidak dapat menghabiskan jatah integrasi Anda yang lain. Defaultnya adalah `60`, dan tidak dapat diatur di atas `300`. |

Anda juga dapat memberikan tanggal `expires_at` pada kunci (ISO 8601, dan harus di masa mendatang). Setelah saat itu, kunci akan berhenti berfungsi dengan sendirinya. Kosongkan jika Anda ingin kunci tidak pernah kedaluwarsa sampai Anda mencabutnya.

> **Penolakan bersifat tertutup.** Jika permintaan berada di luar apa yang diizinkan oleh kunci, permintaan tersebut akan ditolak alih-alih dibiarkan lewat: penulisan dengan kunci hanya-baca akan mengembalikan `403` dengan `error_code: "key_read_only"`, dan apa pun di luar bagian yang diizinkan kunci akan mengembalikan `403` dengan `error_code: "key_scope_denied"`. Jika kunci cakupan mendapatkan `403` yang tidak terduga, endpoint yang Anda panggil tidak termasuk dalam cakupannya — perluas cakupan kunci atau gunakan kunci utama Anda.

> **Hanya pemilik akun yang mengelola kunci.** Keempat endpoint ini memerlukan kunci utama Anda, atau sesi pemilik di aplikasi. Kunci cakupan tidak akan pernah bisa mencantumkan, membuat, mengedit, atau mencabut kunci — termasuk dirinya sendiri — sehingga kunci terbatas tidak akan pernah bisa digunakan untuk membuat kunci yang lebih luas. Mencobanya akan mengembalikan `403` dengan `error_code: "key_scope_denied"`. Karena alasan yang sama, `API Keys` bukanlah bagian yang dapat Anda berikan: memintanya akan mengembalikan `400` dengan `error_code: "invalid_scopes"`.

### Mencantumkan kunci cakupan

Mengembalikan kunci cakupan akun, yang terbaru terlebih dahulu (hingga 200), termasuk yang dicabut sehingga Anda dapat melihat apa yang ditarik dan kapan. Hanya pratinjau tersamar yang dikembalikan — nilai kunci cakupan ditampilkan satu kali, saat pembuatan, dan tidak dapat diambil setelahnya.

`GET /api-keys`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/api-keys" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "api_keys": [
    {
      "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
      "label": "Client dashboard - Acme",
      "key_preview": "abcd...qrst",
      "scopes": {
        "read_only": true,
        "tags": ["Analytics"],
        "sub_account_ids": [],
        "rate_limit_per_min": 60
      },
      "expires_at": null,
      "last_used_at": "2026-08-20T14:03:00.000Z",
      "created_at": "2026-08-14T09:12:00.000Z",
      "revoked_at": null,
      "revoked": false
    }
  ]
}
```

### Membuat kunci cakupan

Membuat kunci cakupan baru dan mengembalikan nilainya **sekali**.

`POST /api-keys`

> **Kunci hanya ditampilkan sekali.** Kunci ini ada dalam respons ini dan tidak ada di tempat lain, selamanya — tidak ada cara untuk mencarinya lagi setelahnya. Simpan kunci tersebut segera setelah Anda menerimanya. Jika Anda kehilangannya, cabut kunci tersebut dan buat yang baru.

**Kolom isi** — semuanya opsional:

| Kolom | Tipe | Catatan |
|---|---|---|
| `label` | string | Nama Anda sendiri untuk kunci tersebut, ditampilkan dalam daftar dan di Pengaturan. |
| `scopes` | object | Empat kolom dalam tabel di atas. Biarkan seluruh objek kosong dan Anda akan mendapatkan default yang aman: hanya-baca, terbatas pada `Analytics`, akun Anda sendiri saja, 60 permintaan per menit. |
| `expires_at` | tanggal ISO 8601 | Kedaluwarsa opsional, harus di masa mendatang. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/api-keys" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Client dashboard - Acme",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics"],
      "sub_account_ids": [],
      "rate_limit_per_min": 60
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    label: "Client dashboard - Acme",
    scopes: { read_only: true, tags: ["Analytics"] },
  }),
});
const data = await res.json();
// Save data.api_key now — it will not be shown again.
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/api-keys",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "label": "Client dashboard - Acme",
        "scopes": {"read_only": True, "tags": ["Analytics"]},
    },
)
data = res.json()
# Save data["api_key"] now — it will not be shown again.
```

**Respons** — `201 Created`

```json
{
  "success": true,
  "api_key": "abcdEFGH1234ijkl5678MNOP9012qrst",
  "key": {
    "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
    "label": "Client dashboard - Acme",
    "key_preview": "abcd...qrst",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics"],
      "sub_account_ids": [],
      "rate_limit_per_min": 60
    },
    "expires_at": null,
    "revoked": false
  },
  "message": "Store this key now — it is shown once and cannot be retrieved again."
}
```

Beberapa detail yang perlu diketahui saat Anda membangun aplikasi berdasarkan ini:

- **Menghilangkan `scopes` tidak sama dengan mengirim daftar `tags` kosong.** Biarkan `scopes` kosong sepenuhnya dan Anda akan mendapatkan default yang aman (hanya-baca, `Analytics` saja). Kirim `"tags": []` dengan sengaja dan kunci tersebut dapat menggunakan setiap bagian — itu dibaca sebagai permintaan yang disengaja untuk kunci tanpa batasan.
- **`read_only` tetap `true` kecuali Anda secara eksplisit mengirim `false`.** Kesalahan ketik atau tanda yang hilang tidak akan pernah secara tidak sengaja menghasilkan kunci yang dapat menulis.

### Memperbarui kunci cakupan

Mengubah label, cakupan, dan/atau kedaluwarsa kunci. Kirim kombinasi apa pun dari ketiganya; mengirim tidak satu pun dari ketiganya akan mengembalikan `400`.

`PATCH /api-keys/{id}`

`{id}` adalah `id` kunci dari daftar (nilai `key_...`), bukan kunci itu sendiri.

> **Cakupan diganti, bukan digabungkan.** Apa pun yang Anda kirim menjadi set izin lengkap kunci tersebut. Itu disengaja: mempersempit kunci tidak akan pernah secara diam-diam membiarkan akses lama yang lebih luas tetap ada. Selalu kirim objek `scopes` lengkap yang Anda inginkan, bukan hanya kolom yang ingin Anda ubah.

Nilai kunci tidak pernah berubah. Tidak ada rotasi di tempat untuk kunci cakupan — untuk menggantinya, buat kunci baru dan cabut kunci lama, sehingga akses kredensial tidak akan pernah berubah di bawah integrasi yang masih memegangnya.

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/api-keys/key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Client dashboard - Acme (read-only)",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics", "Campaigns"],
      "sub_account_ids": [],
      "rate_limit_per_min": 30
    }
  }'
```

**Respons**

```json
{
  "success": true,
  "key": {
    "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
    "label": "Client dashboard - Acme (read-only)",
    "key_preview": "abcd...qrst",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics", "Campaigns"],
      "sub_account_ids": [],
      "rate_limit_per_min": 30
    },
    "expires_at": null,
    "last_used_at": "2026-08-20T14:03:00.000Z",
    "created_at": "2026-08-14T09:12:00.000Z",
    "revoked_at": null,
    "revoked": false
  }
}
```

Jika tidak ada kunci dengan id tersebut di akun Anda, responsnya adalah `404`.

### Mencabut kunci cakupan

Pencabutan bersifat segera: permintaan berikutnya yang menggunakan kunci tersebut akan ditolak dengan `401`. Kunci utama Anda dan setiap kunci cakupan lainnya tidak akan terpengaruh.

`DELETE /api-keys/{id}`

Kunci tersebut tetap ada dalam daftar Anda dengan tanda `"revoked": true`, sehingga Anda tetap memiliki catatan tentang apa yang pernah ada dan apa yang dapat diaksesnya. Mencabut kunci yang sudah dicabut akan berhasil dan tidak mengubah apa pun.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/api-keys/key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "revoked": true,
  "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
  "message": "API key revoked. All requests using it will be rejected immediately."
}
```

---

## Kesalahan API Kunci API

Endpoint kunci API mengembalikan amplop kesalahan standar:

```json
{
  "success": false,
  "error": "No API key found for this account"
}
```

Pada endpoint kunci API, kunci yang hilang atau tidak valid akan mengembalikan `401` dan akun tanpa kunci dalam file akan mengembalikan `404`. Kode bersama yang dapat dikembalikan oleh setiap endpoint — `400`, `403` (paket Anda tidak menyertakan akses API), `429` (batas kecepatan), dan `500` — tercantum beserta panduan percobaan ulang di [Kesalahan & Penomoran Halaman](errors-and-pagination.md).

Titik akhir kunci cakupan menambahkan beberapa kode bernama di kolom `error_code` agar Anda dapat membedakan kasus-kasus tersebut:

| `error_code` | Status | Apa yang terjadi |
|---|---|---|
| `key_read_only` | `403` | Kunci baca-saja mencoba melakukan penulisan. |
| `key_scope_denied` | `403` | Kunci tidak diizinkan pada titik akhir tersebut atau akun terkelola tersebut — atau kunci cakupan mencoba mengelola kunci API, yang tidak pernah diizinkan. |
| `invalid_scopes` | `400` | Cakupan yang diminta menyertakan bagian `API Keys`. Kunci tidak dapat mengelola kunci. |
| `404` | `404` | Tidak ada kunci dengan id tersebut di akun Anda. |

---

## Langkah berikutnya

- [Autentikasi](authentication.md) — empat cara untuk mengautentikasi permintaan, dan bagaimana cakupan kunci diberlakukan.
- [Error & Batas Kecepatan](errors-and-pagination.md) — kode status dan batas 300 permintaan/menit.
