
# API Kampanye

Sebuah kampanye menggabungkan semua yang dibutuhkan bot AI untuk berbicara dengan kontak Anda: instruksinya, saluran yang dijalankannya, jam aktifnya, dan perilaku tindak lanjutnya. API Kampanye memungkinkan Anda untuk mencantumkan, membuat, memperbarui, menduplikasi, mengaktifkan, mengarsipkan, dan menyempurnakan kampanye dari kode Anda sendiri alih-alih dari dasbor.

Semua endpoint di bawah ini bersifat relatif terhadap URL dasar `https://api.youraiconnector.com/v1`. Setiap permintaan harus diautentikasi — lihat [Akses API](../integrations/api-access.md) dan [Autentikasi](authentication.md) untuk mengetahui cara mendapatkan dan mengirimkan kunci API Anda. Akses API adalah fitur berbayar; tanpanya, permintaan akan ditolak dengan `403`.

> **Perhatian:** Beberapa contoh menunjukkan formulir kueri `?apiKey=YOUR_API_KEY` yang sederhana, yang lain menggunakan header `X-API-Key`. Keduanya berfungsi di mana saja — gunakan mana pun yang sesuai dengan pengaturan Anda.

---

## Jenis kampanye

Saat membuat kampanye, Anda harus memilih salah satu dari tipe berikut:

| Tipe | Kegunaan |
|---|---|
| `Incoming from Unknown Contacts` | Bot membalas orang yang mengirim pesan kepada Anda untuk pertama kalinya. |
| `Outgoing` | Bot memulai percakapan dengan kontak yang Anda tambahkan ke kampanye. |
| `Keywords` | **Inert - jangan gunakan.** Kampanye `Keywords` bersifat inert: kampanye ini masih diterima untuk kompatibilitas mundur, tetapi tidak terlihat oleh perutean masuk di saluran mana pun dan tidak ada yang membaca kata kunci pemicunya. Gunakan Titik Masuk bertipe **Kata Kunci** pada Agen AI sebagai gantinya. |
| `Combined` | Campuran perilaku masuk dan keluar. |

**Huruf besar/kecil tidak berpengaruh.** `type`, `status`, `booking_provider`, `first_response_mode`, `bot.anthropic_model` dan `bot.ai_speed` semuanya menerima huruf besar atau kecil apa pun — `"live"`, `"Live"` dan `"LIVE"` adalah hal yang sama — dan nilainya disimpan dalam bentuk kanonisnya, yang merupakan nilai yang dikembalikan saat Anda membaca kampanye tersebut. Satu-satunya pengecualian adalah pasangan jeda: `"Paused"` dan `"paused"` adalah dua status yang benar-benar berbeda, jadi ejaan yang ambigu seperti `"PAUSED"` akan ditolak dengan `400` yang meminta Anda untuk memilih salah satu.

### Dua status jeda

| Status | Siapa yang menulisnya | Apa artinya |
|---|---|---|
| `Paused` | Pemeriksaan keamanan platform itu sendiri (keterlibatan rendah, kesalahan pengiriman berulang, batas tercapai) dan antarmuka Agen serta Siaran yang lebih baru | Kampanye ditahan. Pemindaian terjadwal dapat mencabut jeda keamanan secara otomatis setelah alasannya hilang. |
| `paused` | Tombol Jeda dasbor, dipasangkan dengan `resumed` pada Lanjutkan | Seseorang menjedanya secara manual. Pengiriman terjadwal dibatalkan dan dibangun kembali saat dilanjutkan. |

Keduanya menghentikan kampanye: perutean masuk hanya berjalan saat statusnya tepat `Live`. **Dari API, gunakan `Paused` untuk menjeda dan `Live` untuk melanjutkan** — pasangan huruf kecil ada untuk tombol dasbor dan tetap berfungsi untuk itu.

Tidak satu pun dari ini adalah apa yang terjadi ketika AI berhenti membalas dalam satu percakapan. Itu adalah sakelar per-kontak, `is_bot_active` pada kontak — diatur saat manusia mengambil alih, saat kontak memilih keluar, atau saat AI mengakhiri obrolan. Status kampanye itu sendiri tidak tersentuh, dan setiap percakapan lain di dalamnya terus berjalan. Lihat [jeda atau lanjutkan AI untuk satu kontak](messages.md#pause-or-resume-the-ai-for-one-contact).

> **Membuat kampanye tidak menentukan siapa yang menjawab saluran.** Perutean ditangani oleh **Titik Masuk** pada Agen AI, bukan oleh kampanye. Setiap saluran memiliki satu Titik Masuk default saluran yang menamai Agen yang menjawab kontak baru yang tidak dikenal di saluran tersebut: atur dengan `PUT /entry-points/channel-defaults`, periksa apakah tangga tersebut aktif untuk akun dengan `GET /entry-points/routing-status`, hapus dengan `DELETE /entry-points/channel-defaults`. `POST /channels/campaign` masih menulis peta perutean kampanye per saluran warisan, tetapi peta tersebut tidak lagi dikonsultasikan untuk perutean masuk di akun mana pun; peta tersebut dipertahankan hanya untuk pemulihan (rollback). Jangan membangun berdasarkan peta tersebut. Lihat [Merutekan saluran ke kampanye](channels.md#route-a-channel-to-a-campaign) untuk kedua antarmuka secara berdampingan.

---

## Daftar kampanye

`GET /campaigns`

Mengembalikan kampanye Anda, yang terbaru terlebih dahulu. Kampanye yang diarsipkan tidak disertakan kecuali Anda mengirimkan `archived=true`.

**Parameter kueri**

| Parameter | Wajib | Deskripsi |
|---|---|---|
| `limit` | Tidak | Jumlah maksimum kampanye yang akan dikembalikan. Default `50`, maksimum `100`. |
| `cursor` | Tidak | Kursor penomoran halaman. Kirimkan nilai `next_cursor` dari respons sebelumnya untuk mendapatkan halaman berikutnya. |
| `archived` | Tidak | Atur ke `true` untuk menyertakan kampanye yang diarsipkan. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns?limit=20&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/campaigns?limit=20", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.campaigns, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns",
    params={"limit": 20},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["campaigns"], data["next_cursor"])
```

**Respons**

```json
{
  "success": true,
  "campaigns": [
    {
      "id": "NBCXrhqGPSFsd6MV7pRo",
      "name": "Inbound WhatsApp Leads",
      "type": "Incoming from Unknown Contacts",
      "status": "Live",
      "enabled": true,
      "archived": false,
      "created_at": 1700000000000,
      "ai_mode": true,
      "language": "en",
      "enabled_channels": ["whatsapp", "instagram"]
    }
  ],
  "next_cursor": "NBCXrhqGPSFsd6MV7pRo"
}
```

Saat `next_cursor` adalah `null`, Anda telah mencapai halaman terakhir.

---

## Mendapatkan kampanye

`GET /campaigns/{campaignId}`

Mengembalikan dokumen kampanye lengkap, termasuk konfigurasi bot langsung (`bot`), pengaturan tindak lanjut, saluran yang diaktifkan, dan kata kunci apa pun. Stempel waktu dikembalikan dalam milidetik epoch.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY"
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
campaign = res.json()["campaign"]
```

**Respons**

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "name": "Inbound WhatsApp Leads",
    "type": "Incoming from Unknown Contacts",
    "status": "Live",
    "language": "en",
    "ai_mode": true,
    "enabled": true,
    "archived": false,
    "created_at": 1700000000000,
    "enabled_channels": ["whatsapp", "instagram"],
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call.",
      "ai_speed": "balanced",
      "anthropic_model": "standard",
      "max_messages": 20
    }
  }
}
```

::: note
**Catatan:** Kampanye yang dimiliki oleh akun lain akan mengembalikan `404 Campaign not found` (bukan `403`), sehingga Anda tidak dapat mengetahui apakah suatu ID ada di akun lain.
:::


---

## Membuat kampanye

`POST /campaigns`

Membuat kampanye baru. `name` dan `type` wajib diisi; yang lainnya bersifat opsional. Anda dapat menyertakan kolom kampanye lainnya dalam permintaan yang sama — misalnya `language`, `ai_mode`, atau objek konfigurasi `bot` lengkap — dan itu akan disimpan bersama kampanye baru tersebut. Pemilik dan waktu pembuatan diatur secara otomatis.

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `name` | Ya | Nama kampanye. |
| `type` | Ya | Salah satu dari empat jenis kampanye di atas. |
| `language` | Tidak | Bahasa yang digunakan bot untuk membalas (contoh: `"en"`). |
| `ai_mode` | Tidak | Apakah mode AI aktif (`true`/`false`). Pada kampanye yang dijawab oleh Agen AI, pembacaan akan mengembalikan tombol **Aktif** Agen alih-alih nilai yang tersimpan — lihat catatan di bawah bagian pembaruan. |
| `bot` | Tidak | Objek konfigurasi bot (lihat [Bidang konfigurasi bot](#bot-configuration-fields)). |
| `list_id` | Tidak | ID daftar kontak yang akan dilampirkan. |
| `event_id` | Tidak | ID jenis acara yang dapat dipesan oleh AI. |
| `event_ids` | Tidak | Beberapa jenis acara sekaligus, sebagai larik ID jenis acara — yang pertama adalah default. Kirim `event_id` atau `event_ids`, jangan keduanya. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Spring Promo",
    "type": "Outgoing",
    "language": "en",
    "ai_mode": true,
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call."
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/campaigns", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Spring Promo",
    type: "Outgoing",
    language: "en",
    ai_mode: true,
    bot: {
      instructions: "Greet warmly and ask about their goals.",
      goal: "Book a discovery call.",
    },
  }),
});
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "Spring Promo",
        "type": "Outgoing",
        "language": "en",
        "ai_mode": True,
        "bot": {
            "instructions": "Greet warmly and ask about their goals.",
            "goal": "Book a discovery call.",
        },
    },
)
campaign_id = res.json()["campaign_id"]
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Memperbarui kampanye

`PUT /campaigns/{campaignId}`

Memperbarui kampanye sebagian — kirim hanya kolom yang ingin Anda ubah. Ini adalah satu-satunya kata kerja pembaruan umum; tidak ada `PATCH /campaigns/{campaignId}` (dua rute `PATCH` adalah tombol [aktifkan](#enable-or-disable-a-campaign) dan [arsip](#archive-or-restore-a-campaign) yang sempit).

**Kolom mana yang dapat Anda ubah.** Semua yang ditulis oleh editor kampanye, termasuk `name`, `status`, `type`, `language`, `ai_mode`, `enabled_channels`, pengaturan pemicu dan drip, tanda pemesanan dan tindak lanjut, kolom pemantauan Instagram/Facebook, dan seluruh konfigurasi `bot`. Identitas dan kepemilikan dikunci selama masa pakai kampanye: `user`, `id`, dan `created_at` ditolak, begitu pula nama kolom apa pun yang tidak dikenali oleh titik akhir. Penolakan dilakukan per permintaan, bukan per kolom — satu kunci yang tidak diketahui mengembalikan `400` dan **tidak ada** yang ditulis dalam permintaan tersebut.

**`ai_mode` pada kampanye yang didukung Agen mencerminkan Agen tersebut.** Saat kampanye dijawab oleh Agen AI, membaca kampanye akan mengembalikan `ai_mode` yang berasal dari tombol **Aktif** Agen tersebut — satu sakelar yang benar-benar menentukan apakah AI membalas. Menulis `ai_mode` pada kampanye semacam itu akan diterima tetapi tidak akan mengubah apa yang Anda baca kembali; sebagai gantinya, aktifkan atau nonaktifkan tombol Aktif Agen tersebut (di dasbor, atau melalui API Agen). Pada kampanye klasik tanpa Agen, `ai_mode` membaca dan menulis nilai yang tersimpan seperti sebelumnya.

**Kolom bot digabungkan, tidak ditimpa.** Kirim pengaturan bot baik sebagai kunci bertitik (`"bot.instructions": "..."`) atau sebagai objek bersarang (`"bot": { "instructions": "..." }`) — keduanya menulis per bagian, sehingga kolom yang Anda tinggalkan tetap mempertahankan nilainya saat ini. `bot.instructions`, `bot.goal`, `bot.rules`, dan `bot.personality` semuanya dapat diedit dengan cara ini, begitu pula pengaturan bot lainnya yang tercantum di bawah [Kolom konfigurasi bot](#bot-configuration-fields). Hal yang sama berlaku untuk `test_bot`, `frequency`, dan `follow_up_config`.

Untuk mengganti konfigurasi bot secara keseluruhan — menghapus kolom apa pun yang tidak Anda kirim — gunakan `bot_replace` (atau `test_bot_replace`) dengan objek lengkap. Anda tidak dapat menggabungkan penggantian dan penggabungan untuk objek yang sama dalam satu permintaan; itu akan mengembalikan `400`.

::: note
**Catatan:** Menulis `bot.*` melalui API akan langsung berlaku **segera** pada kampanye yang sedang berjalan. Editor dasbor bekerja secara berbeda: pengeditan di sana disimpan sebagai draf dan hanya ditayangkan saat klien mengeklik Publikasikan. Jadi, jika klien memiliki perubahan dasbor yang belum dipublikasikan, perubahan tersebut tetap berada di `test_bot` dan pembacaan API dari `bot` dengan benar menunjukkan apa yang digunakan AI saat ini.
:::


Beberapa bidang diatur melalui kunci khusus alih-alih ditulis secara langsung: gunakan `list_id` untuk daftar kontak, `event_id` untuk jenis acara (atau `event_ids`, larik terurut ID jenis acara, agar AI dapat memesan beberapa — yang pertama adalah default; larik kosong akan memutuskan tautan semuanya), dan `contact_ids` (larik ID kontak) untuk kontak kampanye. Entri basis pengetahuan dikelola melalui [API FAQ](faqs.md), bukan titik akhir ini.

**Tag menggantikan, tidak menggabungkan.** Kirim `tags` sebagai array lengkap dan itu akan menjadi set tag kampanye — lihat [Tag kampanye](#campaign-tags) untuk kolom dan endpoint yang menambah atau mengedit satu tag.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Spring Promo v2", "enabled_channels": ["whatsapp"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Spring Promo v2",
      enabled_channels: ["whatsapp"],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"name": "Spring Promo v2", "enabled_channels": ["whatsapp"]},
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Menghapus kampanye

`DELETE /campaigns/{campaignId}`

Menghapus kampanye secara permanen. Tindakan ini tidak dapat dibatalkan — jika Anda mungkin memerlukan kampanye tersebut lagi, [arsip](#archive-or-restore-a-campaign) saja.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  { 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/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Respons**

```json
{
  "success": true
}
```

---

## Menduplikasi kampanye

`POST /campaigns/{campaignId}/duplicate`

Membuat salinan kampanye dengan semua pengaturannya tetap terjaga. Salinan tersebut dimulai dalam status **dinonaktifkan** dan namanya mendapatkan akhiran `(copy)`, sehingga tidak akan pernah mengirim pesan sampai Anda mengaktifkannya secara eksplisit.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
new_campaign_id = res.json()["campaign_id"]
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "aZ9plnewCopyId01234"
}
```

> Salinan duplikat **dalam satu akun**.

---


## Mengaktifkan atau menonaktifkan kampanye

`PATCH /campaigns/{campaignId}/enabled`

Menyalakan atau mematikan kampanye. Kampanye yang dinonaktifkan akan berhenti berinteraksi dengan kontak tetapi tetap menyimpan semua konfigurasinya.

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `enabled` | Ya | `true` untuk mengaktifkan, `false` untuk menonaktifkan. Harus berupa boolean. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled",
  {
    method: "PATCH",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ enabled: true }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"enabled": True},
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "enabled": true
}
```

---

## Mengarsipkan atau memulihkan kampanye

`PATCH /campaigns/{campaignId}/archived`

Mengarsipkan atau memulihkan kampanye. Kampanye yang diarsipkan disembunyikan dari daftar kampanye default namun tetap menyimpan semua datanya dan dapat dipulihkan kapan saja.

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `archived` | Ya | `true` untuk mengarsipkan, `false` untuk memulihkan. Harus berupa boolean. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "archived": true }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived",
  {
    method: "PATCH",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ archived: true }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"archived": True},
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "archived": true
}
```

---

## Memperbarui konfigurasi bot

`PUT /campaigns/{campaignId}/bot-config`

Ini adalah cara aman untuk mengubah pengaturan bot secara individual. Setiap kolom yang Anda kirim akan **digabungkan** ke dalam konfigurasi bot yang sudah ada, sehingga kolom apa pun yang Anda lewatkan akan tetap dipertahankan. Gunakan cara ini alih-alih endpoint pembaruan kampanye jika Anda hanya ingin menyesuaikan sebagian dari bot.

Kunci kolom hanya boleh menggunakan huruf, angka, garis bawah, dan tanda hubung.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": "Always answer in a friendly, concise tone.",
    "ai_speed": "balanced"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      instructions: "Always answer in a friendly, concise tone.",
      ai_speed: "balanced",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "instructions": "Always answer in a friendly, concise tone.",
        "ai_speed": "balanced",
    },
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Kolom konfigurasi bot

Semua kolom bot bersifat opsional. Kirimkan hanya kolom yang ingin Anda atur. Kolom bot tambahan apa pun di luar yang tercantum di sini akan diterima dan disimpan sebagaimana adanya.

| Bidang | Tipe | Deskripsi |
|---|---|---|
| `instructions` | string | Instruksi utama yang mengarahkan cara bot berbicara dengan kontak. |
| `rules` | string | Aturan ketat yang harus selalu diikuti oleh bot. |
| `goal` | string | Hasil yang harus diupayakan oleh bot dalam setiap percakapan. |
| `personality` | string | Deskripsi nada suara dan kepribadian untuk bot. |
| `ai_speed` | string | Seberapa banyak penalaran yang diterapkan AI sebelum membalas. Salah satu dari `fast`, `fast_thinker`, `balanced`, `thorough`. |
| `anthropic_model` | string | Tingkat kualitas AI yang digunakan untuk balasan kampanye ini. Salah satu dari `standard`, `economy` (tidak digunakan lagi), `max`, `mini`. `max` dan `mini` hanya berlaku pada akun yang memenuhi syarat untuk tingkat tersebut. |
| `max_messages` | integer | Jumlah maksimum pesan bot per percakapan. |
| `alert_human_when` | string | Kondisi di mana bot harus memberi tahu rekan tim manusia. |
| `availability` | object | Jadwal jam aktif bot. Anda dapat mengaturnya di sini, atau menggunakan [titik akhir jam aktif](#set-the-bot-active-hours) khusus. |
| `follow_up_config` | object | Konfigurasi perilaku tindak lanjut, disimpan sebagaimana disediakan. |

---

## Mengatur jam aktif bot

`PUT /campaigns/{campaignId}/active-hours`

Mengatur jadwal ketersediaan bot. Di luar jendela waktu yang dikonfigurasi, bot tidak akan membalas secara otomatis. Ini akan menulis ke kolom `availability` dari konfigurasi bot.

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `availability` | Ya | Objek yang dikunci berdasarkan hari kerja. Kunci yang diizinkan adalah `monday` hingga `sunday`; kunci lainnya akan mengembalikan `400`. Hari yang Anda lewatkan tidak akan diubah. |

Setiap hari kerja menampung satu jendela waktu atau serangkaian jendela. Sebuah jendela memiliki `start_time` dan `end_time` dalam format `HH:MM` 24 jam.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "availability": {
      "monday": { "start_time": "09:00", "end_time": "17:00" },
      "tuesday": [
        { "start_time": "09:00", "end_time": "12:00" },
        { "start_time": "13:00", "end_time": "17:00" }
      ]
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      availability: {
        monday: { start_time: "09:00", end_time: "17:00" },
        tuesday: [
          { start_time: "09:00", end_time: "12:00" },
          { start_time: "13:00", end_time: "17:00" },
        ],
      },
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "availability": {
            "monday": {"start_time": "09:00", "end_time": "17:00"},
            "tuesday": [
                {"start_time": "09:00", "end_time": "12:00"},
                {"start_time": "13:00", "end_time": "17:00"},
            ],
        }
    },
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Mencantumkan fungsi kustom kampanye

`GET /campaigns/{campaignId}/custom-functions`

Mengembalikan fungsi kustom yang ditautkan ke kampanye ini, yang diselesaikan menjadi definisi lengkap. Fungsi kustom adalah tindakan HTTP eksternal yang dapat dipanggil bot selama percakapan — misalnya, memeriksa stok di toko Anda atau membuat catatan di CRM Anda.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { custom_functions } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
custom_functions = res.json()["custom_functions"]
```

**Respons**

```json
{
  "success": true,
  "custom_functions": [
    {
      "id": "fn_abc123",
      "name": "check_stock",
      "description": "Looks up whether a product is in stock.",
      "url": "https://example.com/api/stock",
      "method": "POST",
      "input": [
        { "name": "sku", "type": "string" }
      ],
      "ai_action": "Tell the customer whether the item is available.",
      "created_at": 1700000000000,
      "updated_at": 1700000500000
    }
  ]
}
```

---

## Menautkan fungsi kustom ke kampanye

`POST /campaigns/{campaignId}/custom-functions`

Menautkan [fungsi kustom](../ai-automation/custom-functions.md) yang sudah ada ke kampanye ini agar bot dapat memanggilnya selama percakapan. Menautkan fungsi yang sudah tertaut tidak akan memberikan efek apa pun.

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `custom_function_id` | Ya | ID fungsi kustom yang akan ditautkan. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "custom_function_id": "fn_abc123" }'
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Membatalkan tautan fungsi kustom dari kampanye

`DELETE /campaigns/{campaignId}/custom-functions/{customFunctionId}`

Membatalkan tautan fungsi yang tidak tertaut tidak akan memberikan efek apa pun.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions/fn_abc123?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Menautkan sumber basis pengetahuan ke kampanye

`POST /campaigns/{campaignId}/kb-sources`

Menautkan sumber basis pengetahuan (dibuat melalui [API FAQ](faqs.md)) ke kampanye ini agar bot dapat menggunakannya saat menjawab. Menautkan sumber yang sudah tertaut tidak akan memberikan efek apa pun.

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `kb_source_id` | Ya | ID sumber basis pengetahuan yang akan ditautkan. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/kb-sources?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kb_source_id": "kb_abc123" }'
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Membatalkan tautan sumber basis pengetahuan dari kampanye

`DELETE /campaigns/{campaignId}/kb-sources/{kbSourceId}`

Membatalkan tautan sumber yang tidak tertaut tidak akan memberikan efek apa pun.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/kb-sources/kb_abc123?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Menautkan server MCP ke kampanye

`POST /campaigns/{campaignId}/mcp-servers`

Menautkan server MCP ke kampanye ini, memberikan bot akses ke alat server tersebut selama percakapan. Menautkan server yang sudah tertaut tidak akan melakukan apa pun.

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `mcp_server_id` | Ya | ID server MCP yang akan ditautkan. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mcp_server_id": "mcp_abc123" }'
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Memutuskan tautan server MCP dari kampanye

`DELETE /campaigns/{campaignId}/mcp-servers/{mcpServerId}`

Memutuskan tautan server yang tidak tertaut tidak akan melakukan apa pun.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers/mcp_abc123?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Pustaka media kampanye

Pustaka media menyimpan gambar, video, dokumen, dan catatan suara yang dapat dikirimkan bot selama percakapan.

### Mencantumkan pustaka media kampanye

`GET /campaigns/{campaignId}/media-library`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "media_items": [
    {
      "id": "media_abc123",
      "item_id": "media_abc123",
      "title": "Pricing sheet",
      "description": "Send when the contact asks about pricing.",
      "media_url": "https://example.com/pricing.pdf",
      "media_content_type": "application/pdf",
      "type": "document",
      "agent_id": "",
      "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
      "media_home": "campaign"
    }
  ]
}
```

`media_url` adalah URL bertanda tangan yang diambil pada saat pengunggahan — URL tersebut mungkin sudah kedaluwarsa saat Anda membacanya kembali; dasbor akan menandatanganinya ulang sesuai permintaan.

### Mengunggah item media

`POST /campaigns/{campaignId}/media-library`

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `base64Data` | Ya | File, dikodekan dalam base64 (tanpa awalan data-URL). |
| `mimeType` | Ya | Tipe MIME file (contoh: `image/png`). |
| `title` | Ya | Label singkat yang ditampilkan di pustaka dan di prompt AI. |
| `description` | Ya | Instruksi yang memberi tahu bot **kapan** harus mengirim item ini. |
| `fileName` | Tidak | Nama file asli, digunakan untuk membuat nama objek penyimpanan. |
| `sendMessage` | Tidak | Kata-kata pilihan yang harus digunakan bot saat mengirim item ini. |
| `maxSendsPerConversation` | Tidak | Jumlah maksimum bot boleh mengirim item ini ke satu kontak dalam satu percakapan. Default-nya adalah `1`. |
| `sendAsVoiceNote` | Tidak | Untuk unggahan audio, transkode menjadi catatan suara WhatsApp. Default-nya adalah `false` (disimpan sebagai file audio biasa). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "base64Data": "iVBORw0KGgoAAAANSUhEUgAA...",
    "mimeType": "image/png",
    "title": "Product photo",
    "description": "Send when the contact asks what the product looks like."
  }'
```

**Respons**

```json
{
  "success": true,
  "itemId": "media_abc123",
  "mediaUrl": "https://example.com/product.png",
  "storagePath": "ai_media/campaigns/NBCXrhqGPSFsd6MV7pRo/media_abc123.png",
  "mediaContentType": "image/png",
  "type": "image",
  "isVoiceNote": false
}
```

### Perbarui item media

`PATCH /campaigns/{campaignId}/media-library/{itemId}`

Hanya mengedit metadata item — untuk mengganti file itu sendiri, hapus item tersebut dan unggah yang baru.

| Bidang | Deskripsi |
|---|---|
| `title` | Label singkat. |
| `description` | Instruksi kapan harus mengirim. |
| `send_message` | Pilihan kata yang disukai untuk digunakan bot. |
| `max_sends_per_conversation` | Bilangan bulat non-negatif, atau `null` untuk menghapus batas. |

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Updated pricing sheet" }'
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "item_id": "media_abc123"
}
```

### Hapus item media

`DELETE /campaigns/{campaignId}/media-library/{itemId}`

Menghapus item yang sudah tidak ada tidak akan melakukan apa pun (no-op).

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{ "success": true, "deleted": true }
```

---

## Tag kampanye

Tag kampanye adalah label yang Anda ajarkan kepada bot untuk diterapkan pada kontak selama percakapan — `hot-lead`, `not-interested`, `booked-a-call`. Setiap tag memiliki tiga bagian:

| Kolom | Tipe | Deskripsi |
|---|---|---|
| `name` | string, wajib | Label itu sendiri. Ini adalah apa yang diterapkan bot pada kontak dan apa yang Anda cocokkan nantinya, jadi buatlah singkat dan stabil. |
| `description` | string | Instruksi yang memberi tahu bot **kapan** harus menerapkan tag ini. Ini adalah bagian yang melakukan pekerjaan — "orang tersebut mengonfirmasi mereka bergabung dengan komunitas" digunakan, "prospek potensial" tidak. |
| `webhook` | string | URL yang menerima `POST` saat tag tersebut disematkan pada kontak. Kosongkan jika Anda tidak memerlukannya. |
| `tag_id` | string | Opsional. Menautkan entri ini ke tag yang sudah ada di akun Anda alih-alih yang baru. Berikan jika Anda ingin menangani tag spesifik ini nanti dengan endpoint satu-tag di bawah. |

Nama tag harus unik dalam satu kampanye. Bot menerapkan tag **berdasarkan nama**, jadi dua entri yang berbagi satu nama tidak memiliki pemenang yang ditentukan.

### Tetapkan semua tag kampanye

`PUT /campaigns/{campaignId}` dengan array `tags`.

Ini menggantikan tag kampanye dengan apa yang Anda kirimkan, yang sama dengan yang dilakukan tab Tag di dasbor saat Anda menyimpannya. **Kirim array lengkap setiap saat** — tag yang Anda lewatkan adalah tag yang Anda hapus. Mengirim `[]` akan menghapus semuanya.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": [
      {
        "name": "hot-lead",
        "description": "The person confirms they want to buy, or asks how to get started right away.",
        "webhook": "https://example.com/hooks/campaign-events"
      },
      {
        "name": "not-interested",
        "description": "The person declines the offer or says they are not a fit."
      }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      tags: [
        {
          name: "hot-lead",
          description:
            "The person confirms they want to buy, or asks how to get started right away.",
          webhook: "https://example.com/hooks/campaign-events",
        },
        {
          name: "not-interested",
          description: "The person declines the offer or says they are not a fit.",
        },
      ],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "tags": [
            {
                "name": "hot-lead",
                "description": "The person confirms they want to buy, or asks how to get started right away.",
                "webhook": "https://example.com/hooks/campaign-events",
            },
            {
                "name": "not-interested",
                "description": "The person declines the offer or says they are not a fit.",
            },
        ]
    },
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

Baca kembali tag dengan [`GET /campaigns/{campaignId}`](#get-a-campaign).

### Tambahkan satu tag

`POST /campaigns/{campaignId}/tags`

Menambahkan satu tag tanpa mengirim ulang sisanya. Gunakan ini saat Anda menambahkan ke set yang tidak Anda buat dalam permintaan ini.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "booked-a-call", "description": "The person confirms a booked time." } }'
```

Memposting tag yang sama persis dua kali tidak akan melakukan apa pun pada kali kedua. Memposting `tag_id` yang sama dengan nama atau deskripsi yang berbeda akan menambahkan entri **kedua** alih-alih mengedit yang pertama — gunakan endpoint di bawah untuk mengedit di tempat.

### Perbarui atau hapus satu tag

`PUT /campaigns/{campaignId}/tags/{tagId}`
`DELETE /campaigns/{campaignId}/tags/{tagId}`

Ini menangani satu entri berdasarkan `tag_id`-nya, jadi ini hanya berfungsi pada tag yang dibuat dengan satu entri. Jika sebuah tag tidak memiliki `tag_id`, ubah dengan `PUT /campaigns/{campaignId}` seluruh array di atas.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags/tag_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "hot-lead", "description": "Updated instruction." } }'
```

`tagId` yang tidak ada di kampanye akan mengembalikan `404` dengan `"Tag not found in campaign tags"`.

---

## Mengaktifkan/menonaktifkan saluran kampanye

`POST /campaigns/{campaignId}/channels`

Menambahkan atau menghapus saluran dari array `enabled_channels` kampanye tanpa mengirim ulang seluruh array — lebih aman daripada [`PUT /campaigns/{campaignId}`](#update-a-campaign) jika ada hal lain yang mungkin sedang mengedit kampanye pada saat yang sama.

Kirim baik satu toggle tunggal atau batch — jangan keduanya dalam permintaan yang sama:

```json
{ "channel": "whatsapp", "action": "add" }
```

```json
{ "add": ["whatsapp", "instagram"], "remove": ["sms"] }
```

| Bidang | Deskripsi |
|---|---|
| `channel` | Satu saluran untuk diaktifkan/dinonaktifkan. Pasangkan dengan `action`. |
| `action` | `"add"` atau `"remove"`. Pasangkan dengan `channel`. |
| `add` | Array saluran untuk ditambahkan. Bentuk batch — gunakan sebagai pengganti `channel`/`action`. |
| `remove` | Array saluran untuk dihapus. Bentuk batch. |

Saluran yang valid: `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `messenger`, `facebook`, `chat_widget`, `custom_channel`, `imessage`, `telegram`, `instagram_private`, `line`, `viber`, `tiktok`, `email`, `linkedin`, `skool`.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/channels?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "whatsapp", "action": "add" }'
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "added": ["whatsapp"],
  "removed": []
}
```

> Ini hanya mengubah saluran mana yang diiklankan oleh kampanye — ini tidak menentukan siapa yang menjawab saluran tersebut. Lihat [Jenis kampanye](#campaign-types) di atas dan [Mengarahkan kampanye ke saluran masuk](#route-a-campaign-to-incoming-channels) di bawah untuk hal tersebut.

---

## Komentar-ke-DM (Instagram dan Facebook)

Komentar-ke-DM mengubah komentar pada salah satu postingan Anda menjadi percakapan pribadi: seseorang berkomentar, bot mengirimkan mereka DM, dan kampanye melanjutkan percakapan dari sana. Fitur ini dikonfigurasi sepenuhnya melalui objek kampanye, jadi tidak ada bagian yang hanya tersedia di UI.

Hubungkan Halaman Facebook terlebih dahulu — lihat [Koneksi Saluran](channels.md#instagram--messenger-meta). Kemudian atur kolom di bawah ini dengan [`PUT /campaigns/{campaignId}`](#update-a-campaign).

> **Kampanye harus `Live`.** Pemantauan komentar hanya mengambil kampanye yang `status`-nya adalah `Live` (huruf besar/kecil apa pun — lihat [Jenis kampanye](#campaign-types)). Status lain apa pun akan menonaktifkannya secara diam-diam, dan status yang dibuat-buat seperti `"Active"` sekarang akan ditolak dengan `400` alih-alih disimpan. Status yang valid mencakup `Draft`, `Pending Approval`, `Scheduled`, `Live`, `Paused`, `Completed`, `Sent` dan `Failed`.

**Kolom**

| Bidang | Tipe | Deskripsi |
|---|---|---|
| `monitor_instagram_posts` | boolean | Pantau setiap kiriman Instagram di halaman yang terhubung. |
| `instagram_post_ids` | string[] | Pantau hanya kiriman Instagram ini. Biarkan tidak disetel saat `monitor_instagram_posts` aktif. |
| `instagram_comment_delay_minutes` | number | Tunggu sekian menit setelah komentar sebelum mengirim DM. |
| `monitor_facebook_posts` | boolean | Pantau setiap kiriman Facebook di halaman yang terhubung. |
| `facebook_post_ids` | string[] | Pantau hanya kiriman Facebook ini. |
| `facebook_comment_delay_minutes` | number | Penundaan sebelum DM, dalam menit. |
| `public_comment_reply_instructions` | string | Panduan untuk balasan terlihat yang ditinggalkan di komentar itu sendiri. Menggantikan kata-kata default "cek DM Anda". |
| `first_response_mode` | string | `"ai"` (default) menghasilkan DM pertama dan balasan publik. `"exact_text"` mengirimkan kata-kata Anda apa adanya, tanpa pembuatan AI dan tanpa biaya kredit. |
| `first_response_exact_text` | string | DM pertama yang apa adanya, digunakan saat `first_response_mode` adalah `"exact_text"`. Diperlukan agar mode tersebut berlaku. |
| `first_response_exact_text_variants` | string[] | Kata-kata tambahan untuk DM pertama. Satu dipilih secara acak per pengiriman, sehingga DM yang berulang tidak identik secara byte. |
| `public_comment_reply_exact_text` | string | Balasan publik yang apa adanya dalam mode `"exact_text"`. Biarkan kosong untuk melewati balasan publik dan hanya mengirim DM. |
| `public_comment_reply_exact_text_variants` | string[] | Kata-kata tambahan untuk balasan publik. |
| `monitor_instagram_followers` | boolean | Perlakukan pengikut baru sebagai pemicu dan kirim DM pembuka (akun pribadi Instagram). |
| `follower_outreach_instructions` | string | Panduan untuk DM pembuka pengikut baru tersebut. |
| `respond_to_instagram_story_replies` | boolean | Apakah AI menjawab balasan ke Cerita Instagram Anda. Default `true`. Setel `false` agar balasan Cerita masuk ke obrolan (dengan Cerita terlampir) tanpa balasan AI. Pengaturan langsung — bukan bagian dari draf, jadi tidak perlu dipublikasikan. |

**Mengosongkan kolom**

Kolom-kolom ini dihapus alih-alih diatur ke `null` saat Anda mengirim `null`, sehingga bot kembali ke default-nya: `instagram_post_ids`, `facebook_post_ids`, `instagram_comment_delay_minutes`, `facebook_comment_delay_minutes`, `public_comment_reply_instructions`, `follower_outreach_instructions`, `first_response_exact_text`, `first_response_exact_text_variants`, `public_comment_reply_exact_text`, `public_comment_reply_exact_text_variants`.

> **Satu kunci yang tidak dikenal akan menolak seluruh permintaan.** `PUT /campaigns/{campaignId}` memvalidasi seluruh isi terhadap daftar yang diizinkan. Kunci yang tidak dikenali akan mengembalikan `400` untuk permintaan tersebut secara keseluruhan — kunci tersebut tidak diabaikan begitu saja, dan tidak ada kolom lain dalam isi tersebut yang ditulis.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "Live",
    "monitor_instagram_posts": true,
    "instagram_comment_delay_minutes": 2,
    "first_response_mode": "exact_text",
    "first_response_exact_text": "Hey! Sending the details over now.",
    "first_response_exact_text_variants": [
      "Hi there, here are the details you asked for.",
      "Thanks for commenting, here is what you need."
    ],
    "public_comment_reply_exact_text": "Just sent you a DM."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      status: "Live",
      monitor_instagram_posts: true,
      instagram_comment_delay_minutes: 2,
      first_response_mode: "ai",
      public_comment_reply_instructions:
        "Tell them to check their message requests folder too.",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "status": "Live",
        "monitor_facebook_posts": True,
        "facebook_post_ids": None,
        "facebook_comment_delay_minutes": 5,
    },
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

> Balasan yang terlihat pada komentar memerlukan fitur balasan komentar pada paket Anda. Tanpanya, DM tetap terkirim dan balasan publik dilewati.

---

## Mengoptimalkan kampanye dengan AI

`POST /campaigns/{campaignId}/optimize`

Menjalankan penulisan ulang AI yang sama seperti alur Optimize dan umpan balik jempol ke bawah di dasbor: mengambil umpan balik Anda, menulis ulang instruksi bot, dan menyiapkan hasilnya sebagai draf revisi baru untuk Anda tinjau.

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `user_feedback` | Salah satu dari keduanya wajib diisi | Umpan balik bentuk bebas yang menjelaskan apa yang perlu ditingkatkan. |
| `thumbs_down_feedback` | Salah satu dari keduanya wajib diisi | Umpan balik yang diambil dari jempol ke bawah pada balasan bot tertentu. |
| `thumbs_down_message` | Tidak | Pesan bot yang dirujuk oleh umpan balik jempol ke bawah. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/optimize?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_feedback": "Make the tone more casual and mention the free trial earlier." }'
```

**Respons** (`202` — penulisan ulang berjalan di latar belakang)

```json
{ "success": true, "campaign_id": "NBCXrhqGPSFsd6MV7pRo" }
```

Lakukan polling pada [`GET /campaigns/{campaignId}`](#get-a-campaign) dan pantau `test_bot.status`: statusnya akan langsung berubah menjadi `"Optimizing"`, lalu kembali ke `"Draft"` setelah penulisan ulang selesai di `test_bot`. Dari sana, statusnya akan berperilaku seperti draf dasbor lainnya — tinjau draf tersebut, lalu publikasikan di dasbor agar aktif. `409` berarti pengoptimalan sedang berjalan untuk kampanye ini.

> Pengoptimalan memakan kredit, sama seperti operasi AI lainnya di akun Anda.

---

## Menetapkan kontak ke kampanye

`POST /campaigns/{campaignId}/contacts/{contactId}/assign`

Memasukkan kontak yang sudah ada ke dalam kampanye dan, jika Anda memintanya, segera mengirimkan pesan pembuka kampanye tersebut. Ini adalah cara untuk mengirim templat WhatsApp kampanye yang disetujui ke satu kontak: templat yang digunakan untuk menyetujui kampanye adalah milik kampanye tersebut, sehingga tidak muncul di pustaka [Templates API](templates.md) dan tidak dapat dikirim melalui `/whatsapp-templates/send`.

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `sendOpeningMessage` | Tidak | `true` mengirimkan pesan pembuka kampanye (templat WhatsApp yang disetujui pada kampanye WhatsApp) segera setelah kontak ditetapkan. Default-nya adalah `false`. |
| `triggerAIResponse` | Tidak | `true` membiarkan AI menulis pesan pertamanya sendiri sebagai gantinya. Default-nya adalah `false`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/contacts/contact_abc123/assign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sendOpeningMessage": true }'
```

**Respons**

```json
{
  "success": true,
  "data": { "contactId": "contact_abc123", "campaignId": "NBCXrhqGPSFsd6MV7pRo" }
}
```

> **Kredit:** Mengirim pesan pembuka pada kampanye WhatsApp dikenakan biaya seperti pengiriman templat lainnya, dengan harga berdasarkan negara penerima dan kategori templat. Pada saluran lain, pesan pembuka adalah pesan keluar biasa.

---

## Mengarahkan kampanye ke saluran masuk

Endpoint ini mengelola kampanye mana yang menjawab kontak baru yang tidak dikenal di suatu saluran. **Gunakan Entry Points** untuk integrasi baru (lihat catatan di bawah [Jenis kampanye](#campaign-types)) — endpoint ini tetap berguna untuk menangani kampanye yang menggunakan metode pengarahan lama, dan untuk menyelesaikan konflik kepemilikan saluran antara dua kampanye masuk.

### Menetapkan kampanye ke saluran masuk

`POST /campaigns/{campaignId}/incoming-routing`

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `channels` | Ya | Larik saluran yang harus dijawab oleh kampanye ini untuk kontak baru yang tidak dikenal. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channels": ["whatsapp", "instagram"] }'
```

**Respons**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channels": ["whatsapp", "instagram"],
  "failed": []
}
```

`channels` hanya mencantumkan saluran yang benar-benar diarahkan ke kampanye ini; `failed` mencantumkan saluran yang tidak diarahkan. Jika setiap saluran yang diminta gagal, permintaan itu sendiri akan gagal.

### Menghapus pengarahan masuk kampanye

`DELETE /campaigns/{campaignId}/incoming-routing`

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `channelToUnassign` | Tidak | Hapus pengarahan hanya untuk satu saluran ini. Abaikan untuk menghapus setiap saluran yang saat ini dijawab oleh kampanye ini. |

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channelToUnassign": "instagram" }'
```

**Respons**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channelsRemoved": ["instagram"]
}
```

### Mengaktifkan kembali kampanye yang tidak aktif

`POST /campaigns/{campaignId}/reactivate`

Mengembalikan kampanye dari `Ended`, `Completed`, `Paused`, atau `Draft` dan mengklaim kembali salurannya. Hanya berfungsi pada kampanye `Incoming from Unknown Contacts` atau `Combined` — kampanye yang sudah `Live` dianggap berhasil dan tidak perlu dilakukan apa pun.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/reactivate?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "data": {
    "success": true,
    "channelsReactivated": ["whatsapp"],
    "channelsBlockedByConflict": [],
    "campaignType": "Incoming from Unknown Contacts"
  }
}
```

Saluran yang sudah diklaim oleh agen kampanye lain akan muncul di `channelsBlockedByConflict` alih-alih menggagalkan seluruh panggilan — gunakan [hentikan kampanye masuk yang berkonflik](#stop-a-conflicting-incoming-campaign) di bawah untuk membebaskannya terlebih dahulu jika Anda ingin kampanye ini mengambil alih saluran tersebut. `400` akan dikembalikan untuk jenis kampanye yang tidak mendukung pengaktifan kembali, atau status yang bukan merupakan salah satu status tidak aktif di atas.

### Hentikan kampanye masuk yang berkonflik

`POST /campaigns/{campaignId}/stop-incoming`

Membebaskan saluran kampanye ini dari kampanye LAIN mana pun yang saat ini menahannya, sehingga kampanye ini dapat mengklaimnya berikutnya. Ini adalah versi REST dari apa yang dilakukan dasbor secara otomatis saat Anda meluncurkan kampanye masuk ke saluran yang sudah dijawab oleh orang lain.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/stop-incoming?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "ended_campaign_ids": [],
  "released_channels": ["whatsapp"],
  "cleared_entire_field": false
}
```

`released_channels` akan kembali kosong jika kampanye ini sudah memiliki setiap saluran yang diiklankannya — tidak ada yang perlu diambil alih.

---

## Estimasi biaya

Estimasi biaya peluncuran kampanye sebelum Anda mengirimkannya.

### Estimasi biaya templat WhatsApp

`GET /campaigns/{campaignId}/template-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/template-cost-estimate?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "billing_mode": "credits",
  "data": {
    "countries": [
      {
        "countryCode": "1",
        "name": "United States",
        "iso": "US",
        "flag": "🇺🇸",
        "contactCount": 120,
        "costPerContact": 2,
        "subtotal": 240
      }
    ],
    "totalContacts": 120,
    "totalTemplateCost": 240,
    "templateCategory": "marketing",
    "billing_mode": "credits",
    "service_messages_billable_soon": false
  }
}
```

`billing_mode` adalah `"credits"` pada jalur WhatsApp terkelola. Pada jalur di mana Meta menagih Akun Bisnis WhatsApp Anda sendiri secara langsung, `costPerContact`, `subtotal`, dan `totalTemplateCost` akan kembali `null` — tidak pernah `0`, yang akan dibaca sebagai gratis — karena tidak ada angka kredit yang dilaporkan.

### Estimasi biaya SMS

`GET /campaigns/{campaignId}/sms-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/sms-cost-estimate?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "billing_mode": "twilio_direct",
  "data": {
    "totalContacts": 120,
    "messageLength": 87,
    "segmentsPerMessage": 1,
    "totalSegments": 120,
    "estimatedCostUsd": 0.96,
    "priceUnit": "USD per segment",
    "billedByTwilio": true
  }
}
```

SMS selalu dikirim melalui akun Twilio Anda sendiri (lihat [penyedia SMS](../settings/sms-provider.md)), jadi ini selalu ditagih oleh Twilio secara langsung — `estimatedCostUsd` adalah estimasi tagihan Twilio tersebut, bukan biaya kredit.

---

## Pemeriksaan batas

Periksa batas sebelum Anda meluncurkan, alih-alih baru mengetahuinya setelah pengiriman gagal.

### Pemeriksaan lingkup kampanye

`GET /campaigns/{campaignId}/limits/ai-credit-messaging` — apakah meluncurkan atau menjadwalkan kampanye ini akan melebihi batas pesan kredit AI akun Anda.

`GET /campaigns/{campaignId}/limits/messaging` — apakah hal tersebut akan melebihi batas pesan harian akun Anda.

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/limits/messaging?apiKey=YOUR_API_KEY"
```

**Respons** (batas tidak terlampaui)

```json
{
  "success": true,
  "data": "Campaign is within the daily messaging limit."
}
```

Sebuah `400` akan dikembalikan sebagai gantinya jika batas terlampaui, dengan alasan yang tercantum dalam `error`.

### Pemeriksaan lingkup akun

`GET /campaigns/limits/campaigns` — apakah Anda telah mencapai batas pembuatan kampanye bulanan langganan Anda.

`GET /campaigns/limits/contacts` — apakah Anda telah mencapai batas kontak langganan Anda.

```bash
curl "https://api.youraiconnector.com/v1/campaigns/limits/campaigns?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "data": "You can create 3 more campaigns this month."
}
```

---

## Total statistik kampanye

`GET /campaigns/stats/totals`

Total pesan terkirim dan balasan untuk setiap kampanye DAN setiap agen AI di akun Anda, selama periode waktu tertentu — angka yang sama dengan yang ditampilkan di halaman daftar kampanye di sebelah setiap baris, dalam satu panggilan alih-alih satu permintaan per kampanye.

| Parameter kueri | Deskripsi |
|---|---|
| `days` | Ukuran periode waktu, 1-365. Default-nya adalah 90. |

```bash
curl "https://api.youraiconnector.com/v1/campaigns/stats/totals?days=30&apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "byCampaign": {
    "NBCXrhqGPSFsd6MV7pRo": { "sent": 1204, "replied": 318 }
  },
  "byAgent": {
    "agent_abc123": { "sent": 1204, "replied": 318 }
  },
  "windowDays": 30
}
```

`byAgent` adalah ringkasannya sendiri, bukan jumlah dari `byCampaign` — lalu lintas akun asli Agen AI mungkin tidak memiliki kampanye sama sekali, sehingga jika tidak, hal tersebut tidak akan terlihat di sini.

---

## Menguji kampanye di playground

Playground memungkinkan Anda melakukan percakapan dengan bot kampanye tanpa menyentuh saluran nyata atau kontak nyata. Ini adalah sandbox yang sama dengan panel uji coba dasbor, dan sepenuhnya tersedia melalui API.

Alurnya adalah: buat kontak uji tersembunyi, kirim pesan, lalu polling kampanye untuk mendapatkan balasan bot. Balasan dihasilkan secara asinkron, sehingga balasan tersebut tiba di `test_messages` pada kampanye, bukan di isi respons.

> **Playground berjalan menggunakan kredit biaya API.** Percakapan uji coba yang dimulai dengan kunci API akan dikenakan biaya sesuai tarif pesan AI normal, sama seperti balasan nyata, dan akan muncul di riwayat penggunaan Anda sebagai entri reguler. Pengujian dari dasbor tetap gratis. Perbedaannya disengaja: uji coba melakukan pekerjaan AI yang sama dengan penggunaan langsung, jadi playground API tanpa batas akan menjadi cara untuk menjalankan AI tanpa batas dengan biaya orang lain.

### Langkah 1 - Buat kontak uji

`POST /campaigns/{campaignId}/try-out/contact`

Membuat kontak pengujian tersembunyi dan menautkannya ke kampanye. Semua kolom isi bersifat opsional; apa pun yang Anda lewatkan akan kembali ke identitas sampel bawaan (John Doe).

| Kolom | Wajib | Deskripsi |
|---|---|---|
| `first_name` | Tidak | Nama depan kontak pengujian. |
| `last_name` | Tidak | Nama belakang kontak pengujian. |
| `email` | Tidak | Email kontak pengujian. |
| `phone` | Tidak | Nomor telepon kontak pengujian. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/contact?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "first_name": "Maria", "last_name": "Lopez" }'
```

**Respons**

```json
{
  "success": true,
  "contactId": "8kQx1vNbA2fLpR7d"
}
```

### Langkah 2 - Rekam pesan masuk

`POST /campaigns/{campaignId}/try-out/messages`

Menambahkan pesan ke utas pengujian. Kirim pesan pengunjung ke sini terlebih dahulu, agar muncul di riwayat percakapan yang dibaca bot.

| Kolom | Wajib | Deskripsi |
|---|---|---|
| `messages` | Ya | Larik objek pesan, maksimal 200 per permintaan. |
| `messages[].body` | Ya | Teks pesan. |
| `messages[].direction` | Ya | `"inbound"` untuk pengunjung, `"outbound"` untuk bot. |
| `messages[].timestamp` | Tidak | String ISO-8601 atau milidetik epoch. |
| `messages[].role` | Tidak | Label peran opsional. |
| `messages[].name` | Tidak | Nama tampilan opsional. |
| `ignoreCounter` | Tidak | Bilangan bulat. Mengatur ulang penghitung abaikan kampanye dalam penulisan yang sama. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/messages?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "body": "Do you ship to Belgium?",
        "direction": "inbound",
        "timestamp": "2026-07-22T09:30:00Z"
      }
    ]
  }'
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "appended": 1
}
```

### Langkah 3 - Minta bot untuk membalas

`POST /campaigns/{campaignId}/try-out/test-message`

Mengirimkan pesan ke alur kerja AI. Ini adalah panggilan yang sebenarnya menghasilkan respons bot.

| Kolom | Wajib | Deskripsi |
|---|---|---|
| `message` | Ya | Teks pesan terbaru pengunjung. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/test-message?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Do you ship to Belgium?" }'
```

**Respons**

```json
{
  "success": true,
  "data": "Published"
}
```

`"Published"` berarti pesan telah masuk ke alur kerja AI. `"Ignored"` berarti pesan pengujian yang lebih baru menggantikan pesan ini — playground menggabungkan rentetan pesan cepat menjadi satu balasan, kira-kira empat detik setelah pesan terakhir, sama seperti percakapan nyata yang menunggu seseorang selesai mengetik. Karena jendela penggabungan tersebut, panggilan ini membutuhkan beberapa detik untuk memberikan hasil.

### Langkah 4 - Baca balasan

`GET /campaigns/{campaignId}`

Balasan bot ditambahkan ke larik `test_messages` kampanye. Lakukan polling pada kampanye hingga entri `outbound` baru muncul.

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "test_messages": [
      { "body": "Do you ship to Belgium?", "direction": "inbound" },
      { "body": "Yes, we ship across the EU.", "direction": "outbound" }
    ]
  }
}
```

### Atur ulang playground

`POST /campaigns/{campaignId}/try-out/reset`

Membersihkan seluruh sandbox: menghapus kontak uji, mengosongkan `test_messages`, dan melepaskan kunci respons bot. Gunakan ini di antara pengujian.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/reset?apiKey=YOUR_API_KEY"
```

**Respons**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Endpoint playground lainnya

| Endpoint | Fungsi |
|---|---|
| `DELETE /campaigns/{campaignId}/try-out/contact` | Hanya menghapus kontak uji saat ini dan membatalkan tautannya, membiarkan `test_messages` tetap utuh. Berhasil meskipun tidak ada kontak yang ditautkan. |
| `POST /campaigns/{campaignId}/try-out/transfer` | Memulai playground baru yang diisi dengan percakapan yang sudah ada, dalam satu permintaan: mengganti kontak uji dan menimpa `test_messages`. Body menerima `first_name`, `last_name`, `messages` (bisa kosong), dan `ignoreCounter`. Gunakan cara ini daripada menghapus-lalu-membuat-lalu-menambahkan, yang akan melipatgandakan penggunaan batas laju (rate-limit) Anda. |
| `POST /campaigns/{campaignId}/try-out/messages/replace` | Menimpa `test_messages` secara keseluruhan alih-alih menambahkan. Gunakan untuk memotong atau memutar balik utas. |
| `POST /campaigns/{campaignId}/try-out/contact/reset-ignore-counter` | Hanya mengatur ulang penghitung abaikan kontak uji, untuk alur redo dan ulangi setelah pengiriman. |

---

## Kesalahan API Kampanye

Titik akhir kampanye mengembalikan amplop kesalahan standar:

```json
{
  "success": false,
  "error": "Campaign not found"
}
```

| Status | Kapan ini terjadi pada endpoint kampanye |
|---|---|
| `400` | Bidang yang wajib diisi tidak ada atau tidak valid (misalnya `type` yang salah, `enabled` yang bukan boolean, atau kunci hari kerja yang tidak dikenal). Juga dikembalikan oleh endpoint [pemeriksaan batas](#limit-checks) ketika batas akan terlampaui, dan oleh [aktifkan kembali](#reactivate-a-dormant-campaign) untuk jenis atau status kampanye yang tidak mendukungnya. |
| `404` | Kampanye tidak ditemukan — entah karena tidak ada atau milik akun lain. |
| `409` | [Optimasi](#optimize-a-campaign-with-ai) sudah berjalan untuk kampanye ini. |

Kode bersama yang dapat dikembalikan oleh setiap endpoint — `401`, `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).

---

## Terkait

- [Arahkan saluran ke kampanye](channels.md#route-a-channel-to-a-campaign) — arahkan Instagram, WhatsApp, atau saluran lainnya ke Agen AI yang seharusnya menjawabnya, menggunakan Titik Masuk (Entry Points).
- [Hasilkan templat tindak lanjut dengan AI](templates.md#generate-follow-up-templates-with-ai) — mulai pekerjaan latar belakang yang menulis templat tindak lanjut WhatsApp untuk kampanye.
- [API FAQ](faqs.md) — kelola entri tanya jawab yang digunakan oleh kampanye Anda.
- [Akses API](../integrations/api-access.md) — buat kunci API Anda.
- [Autentikasi](authentication.md) — semua cara untuk mengirimkan kunci Anda.
