
# API Tim

Tim Anda adalah semua orang yang bekerja di dalam akun Anda selain Anda sendiri — admin, agen, dan penampil (read-only) — ditambah undangan yang telah Anda kirim dan departemen tempat Anda mengelompokkan mereka. API Tim adalah versi terprogram dari **Pengaturan → Tim**: menambah dan menghapus orang, mengatur apa yang dapat dilihat dan dilakukan oleh masing-masing orang, mengirim dan menindaklanjuti undangan, serta mengelola departemen.

Semua endpoint di bawah ini bersifat relatif terhadap URL dasar `https://api.youraiconnector.com/v1`. Untuk versi dasbor dari semua yang ada di halaman ini, lihat [Manajemen Tim](../settings/team-management.md).

---

## Autentikasi: endpoint ini memerlukan orang yang sudah masuk (signed-in)

**Ini adalah satu bagian dari API yang tidak dapat digunakan oleh kunci API.** Setiap endpoint `/team` kecuali endpoint [departemen](#departments) harus dipanggil dengan **token ID Firebase** dari sesi yang sudah masuk:

```
Authorization: Bearer <Firebase ID token>
```

Kirim kunci API sebagai gantinya dan permintaan akan ditolak dengan `401`:

```json
{
  "success": false,
  "error_code": 401,
  "error": "This endpoint requires a Firebase ID token (Authorization: Bearer <token>)."
}
```

Alasannya adalah endpoint ini memutuskan apa yang harus dilakukan berdasarkan **siapa yang masuk**: peran Anda, batasan atas dari apa yang diizinkan untuk Anda berikan kepada orang lain, dan apakah Anda saat ini sedang bekerja di dalam akun lain. Kunci API adalah integrasi, bukan orang, jadi tidak ada siapa pun yang dapat menerapkan aturan tersebut.

Dalam praktiknya, itu berarti API Tim ditujukan untuk aplikasi pihak pertama dengan pengguna <span data-t="appName">Your AI Connector</span> yang sudah masuk (lihat [Autentikasi → Token ID Firebase](authentication.md#4-firebase-id-token-first-party-only)). Integrasi server-ke-server tidak dapat mengelola anggota tim — tidak ada cara untuk membuat salah satu token ini dari luar aplikasi.

> **Pengecualian:** keempat endpoint [departemen](#departments) adalah endpoint API biasa. Endpoint tersebut menerima kunci API Anda persis seperti bagian API lainnya, serta sesi yang sudah masuk.

Setiap respons di halaman ini mengikuti amplop (envelope) yang biasa: `success: true` ditambah kolom endpoint di tingkat teratas, atau `success: false` dengan `error` dan `error_code` ketika terjadi kesalahan.

---

## Peran dan izin

Setiap anggota tim memiliki satu **peran**, yang menetapkan akses default mereka di 12 area aplikasi. Anda kemudian dapat mengganti (override) pengaturan untuk area individu.

| Peran | Nilai | Ringkasan |
|---|---|---|
| Admin | `admin` | Semuanya kecuali tindakan tingkat penagihan pemilik. |
| Editor | `editor` | Dapat membuat dan mengubah berbagai hal. Ditampilkan sebagai **Agen** di aplikasi. |
| Viewer | `viewer` | Hanya baca (read-only). |

Setiap area diatur ke salah satu dari empat tingkat: `none` (tersembunyi), `view` (hanya baca), `edit` (buat dan ubah), `full` (termasuk menghapus).

| Area | Admin | Editor | Viewer |
|---|---|---|---|
| `campaigns` | full | edit | view |
| `contacts` | full | edit | view |
| `messages` | full | edit | view |
| `appointments` | full | edit | view |
| `settings` | edit | view | none |
| `billing` | edit | none | none |
| `team_management` | edit | none | none |
| `analytics` | full | view | view |
| `phone_numbers` | edit | none | none |
| `integrations` | edit | none | none |
| `faqs` | full | edit | view |
| `daily_summaries` | full | view | view |

Untuk menyimpang dari default peran, kirim `permission_overrides` — sebuah array objek `{ "area": ..., "level": ... }`. Setiap entri menggantikan default peran untuk area tersebut; semua yang tidak Anda cantumkan akan tetap menggunakan default peran.

```json
"permission_overrides": [
  { "area": "analytics", "level": "full" },
  { "area": "billing", "level": "none" }
]
```

**Siapa yang dapat memanggil endpoint ini**

- **Pemilik akun** selalu dapat melakukan segalanya.
- Anggota tim memerlukan `team_management` di `view` untuk membaca daftar anggota dan daftar undangan, serta di `edit` untuk menambah, mengubah, menangguhkan, menghapus, mengundang, membatalkan, atau mengirim ulang. Admin memiliki `edit` secara default; editor dan penampil memiliki `none`, jadi secara default hanya admin yang dapat mengelola tim.
- **Tidak ada yang dapat memberikan akses di atas akses mereka sendiri.** Jika Anda mencoba memberikan level kepada seseorang yang tidak Anda miliki sendiri — atau mengedit, menangguhkan, atau menghapus seseorang yang aksesnya sudah lebih luas daripada Anda — permintaan akan ditolak dengan `403` dan pesan yang menyebutkan area tersebut.

---

## Objek anggota tim

`GET /team/members` mengembalikan salah satu dari ini per anggota:

| Bidang | Tipe | Deskripsi |
|---|---|---|
| `member_uid` | string | ID pengguna anggota itu sendiri. Ini adalah `{memberUid}` di jalur di bawah. |
| `account_owner_uid` | string | Akun tempat mereka menjadi anggota. |
| `member_email` | string | Alamat email mereka. |
| `member_display_name` | string | Nama yang ditampilkan untuk mereka di aplikasi. |
| `role` | string | `admin`, `editor` atau `viewer`. |
| `permission_overrides` | array | Pengecualian per-area mereka. `[]` jika mereka murni menggunakan default peran. |
| `status` | string | `active` atau `suspended`. |
| `auto_assign_enabled` | boolean \| null | Apakah kontak baru dapat ditetapkan secara otomatis kepada mereka. `null` berarti tidak pernah diubah, yang berperilaku sebagai `true`. |
| `created_by` | string | Siapa yang menambahkan mereka. |
| `created_at` | string \| null | Stempel waktu ISO 8601. |
| `updated_at` | string \| null | Stempel waktu ISO 8601. |

Anggota yang dihapus tidak dikembalikan — daftar ini hanya berisi anggota aktif dan yang ditangguhkan.

> **Batas visibilitas hanya bersifat tulis di sini.** `contact_scope`, `contact_scope_axes` dan `sub_account_access` (lihat [Membatasi apa yang dapat dilihat anggota](#limiting-what-a-member-can-see)) dapat diatur saat membuat, memperbarui, dan mengundang, tetapi endpoint ini tidak mengembalikannya.

---

## Daftar anggota tim

`GET /team/members`

Mengembalikan daftar anggota beserta jumlah kursi paket Anda, sehingga Anda dapat menampilkan "3 dari 5 kursi" dan mengetahui kapan undangan akan ditolak.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/team/members" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/members", {
  headers: { Authorization: `Bearer ${idToken}` },
});
const { members, seat_limit, seats_used } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/team/members",
    headers={"Authorization": f"Bearer {id_token}"},
)
data = res.json()
```

**Respons**

```json
{
  "success": true,
  "members": [
    {
      "account_owner_uid": "owner_uid_123",
      "member_uid": "uid_alice",
      "member_email": "alice@example.com",
      "member_display_name": "Alice Chen",
      "role": "admin",
      "permission_overrides": [],
      "status": "active",
      "auto_assign_enabled": true,
      "created_by": "owner_uid_123",
      "created_at": "2026-05-01T10:00:00.000Z",
      "updated_at": "2026-06-02T09:15:00.000Z"
    }
  ],
  "seat_limit": 5,
  "seats_used": 3
}
```

`seat_limit` adalah `null` ketika paket Anda tidak memiliki batas kursi. `seats_used` hanya menghitung anggota **aktif** — menangguhkan atau menghapus seseorang akan segera mengosongkan kursi mereka.

---

## Menambahkan anggota tim secara langsung

`POST /team/members`

Memasukkan seseorang ke tim Anda secara langsung, tanpa undangan.

> **Ini tidak mengirim email apa pun.** Tidak ada yang diberi tahu bahwa mereka telah ditambahkan, dan jika mereka belum memiliki login <span data-t="appName">Your AI Connector</span>, akun yang dibuat untuk mereka **tidak memiliki kata sandi**, sehingga mereka tidak dapat masuk sampai mereka menyetel ulang kata sandi tersebut. Gunakan [Kirim undangan](#send-an-invitation) kecuali Anda memiliki cara sendiri untuk memberi tahu orang tersebut dan membantu mereka masuk.

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `email` | Ya | Alamat email rekan tim. |
| `display_name` | Ya | Nama yang ditampilkan untuk mereka di aplikasi. |
| `role` | Ya | `admin`, `editor` atau `viewer`. |
| `permission_overrides` | Tidak | Pengecualian per-area terhadap default peran. |
| `contact_scope` | Tidak | `all` atau `assigned` — lihat [Membatasi apa yang dapat dilihat anggota](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | Tidak | Dengan `assigned`, izinkan mereka juga melihat kontak yang belum dimiliki siapa pun. |
| `contact_scope_axes` | Tidak | Batasi mereka pada agen, saluran, atau departemen tertentu. |
| `sub_account_access` | Tidak | Khusus agensi — sub-akun klien mana yang boleh mereka buka. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/members" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "sam@example.com",
    "display_name": "Sam Rivera",
    "role": "editor"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/members", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${idToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "sam@example.com",
    display_name: "Sam Rivera",
    role: "editor",
  }),
});
const { member_uid } = await res.json();
```

**Respons** — `201 Created`

```json
{
  "success": true,
  "team_member_id": "owner_uid_123_uid_sam",
  "member_uid": "uid_sam",
  "message": "Team member created successfully."
}
```

| Status | Kapan |
|---|---|
| `400` | `email`, `display_name` atau `role` tidak ada, peran bukan salah satu dari ketiganya, atau Anda mencoba menambahkan diri sendiri. |
| `403` | Anda tidak memiliki izin untuk mengelola tim, atau Anda mencoba memberikan akses yang lebih tinggi dari milik Anda sendiri. |
| `409` | Orang tersebut sudah menjadi anggota aktif tim Anda. |
| `429` | Kursi tim paket Anda sudah penuh. |

Menambahkan seseorang yang sebelumnya **ditangguhkan atau dihapus** akan mengaktifkan kembali mereka alih-alih gagal.

---

## Memperbarui anggota tim

`PATCH /team/members/{memberUid}`

Mengubah peran, izin, visibilitas, akses klien, atau apakah anggota tersebut berpartisipasi dalam penugasan kontak otomatis. Kirim hanya bidang yang ingin Anda ubah; apa pun yang Anda abaikan akan tetap mempertahankan nilainya saat ini.

**Bidang permintaan**

| Bidang | Deskripsi |
|---|---|
| `role` | `admin`, `editor` atau `viewer`. |
| `permission_overrides` | Menggantikan seluruh daftar pengecualian mereka. Kirim `[]` untuk mengembalikan mereka ke default peran murni. |
| `status` | Hanya `active` yang diterima, untuk mengaktifkan kembali anggota yang ditangguhkan. Untuk menangguhkan seseorang, gunakan [endpoint tangguhkan](#suspend-a-team-member). |
| `auto_assign_enabled` | `true` atau `false`. |
| `contact_scope` | `all` atau `assigned`. |
| `contact_scope_unassigned` | `true` atau `false`. |
| `contact_scope_axes` | Lihat [Membatasi apa yang dapat dilihat anggota](#limiting-what-a-member-can-see). |
| `sub_account_access` | Khusus agensi. |

> **Ini adalah satu-satunya endpoint di mana `null` berarti "hapus".** Mengirim `"contact_scope": null`, `"contact_scope_axes": null` atau `"sub_account_access": null` akan menghapus batasan tersebut sepenuhnya dan mengembalikan anggota agar dapat melihat semuanya. Saat membuat dan mengundang, `null` hanya berarti "tidak disediakan".

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/team/members/uid_sam" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "role": "admin",
    "permission_overrides": [{ "area": "billing", "level": "none" }]
  }'
```

**Respons**

```json
{
  "success": true,
  "message": "Team member updated successfully."
}
```

| Status | Kapan |
|---|---|
| `400` | Nilai `status` atau `auto_assign_enabled` tidak valid, atau Anda mencoba mengaktifkan kembali anggota yang telah dihapus (anggota yang dihapus harus diundang kembali). |
| `403` | Anda tidak memiliki izin, atau perubahan tersebut akan mengedit atau membuat akses yang lebih luas daripada milik Anda sendiri. |
| `404` | Anggota tim tersebut tidak ada. |

---

## Menangguhkan anggota tim

`POST /team/members/{memberUid}/suspend`

Menangguhkan seseorang: mereka tetap berada di tim tetapi kehilangan akses. Gunakan ini alih-alih menghapus jika penangguhan bersifat sementara — aktifkan kembali mereka dengan `PATCH /team/members/{memberUid}` dan `{"status": "active"}`.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/members/uid_sam/suspend" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Respons**

```json
{
  "success": true,
  "message": "Team member suspended successfully."
}
```

Anggota yang ditangguhkan **mengosongkan kursi mereka**, sehingga Anda dapat mengundang orang lain sebagai penggantinya. Akses mereka berakhir saat token sesi mereka saat ini diperbarui, yang dapat memakan waktu hingga satu jam — hapus mereka jika Anda ingin akses tersebut segera berakhir.

| Status | Kapan |
|---|---|
| `400` | Anda mencoba menangguhkan pemilik akun, atau anggota yang sudah ditangguhkan atau dihapus. |
| `403` | Akses mereka lebih luas daripada milik Anda. |
| `404` | Anggota tim tersebut tidak ada. |

---

## Menghapus anggota tim

`DELETE /team/members/{memberUid}`

Menghapus seseorang dari tim Anda dan mengosongkan kursinya. Mereka akan dikeluarkan (signed out) dan kehilangan akses ke akun Anda; login mereka sendiri tidak akan terpengaruh.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/team/members/uid_sam" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Respons**

```json
{
  "success": true,
  "message": "Team member removed successfully."
}
```

Penghapusan bersifat permanen dari sisi Anda: anggota yang telah dihapus **tidak dapat diaktifkan kembali** dengan endpoint pembaruan — undang mereka kembali jika Anda berubah pikiran. Email mereka juga akan dihapus dari daftar notifikasi akun Anda.

| Status | Kapan |
|---|---|
| `400` | Anda mencoba menghapus pemilik akun. |
| `403` | Akses mereka lebih luas daripada akses Anda. |
| `404` | Anggota tim tersebut tidak ada. |

---

## Membatasi apa yang dapat dilihat anggota

Tiga kolom opsional, yang diterima pada [tambah](#add-a-team-member-directly), [perbarui](#update-a-team-member), dan [undang](#send-an-invitation), menentukan seberapa banyak bagian akun yang dapat dilihat seseorang. Kolom-kolom ini bersifat kumulatif: anggota yang dibatasi pada lebih dari satu kategori akan dibatasi oleh semuanya.

**`contact_scope`** — `all` (default: setiap kontak dan percakapan) atau `assigned` (hanya yang ditugaskan kepada mereka). Dengan `assigned`, tambahkan `"contact_scope_unassigned": true` untuk juga mengizinkan mereka melihat kontak yang belum dimiliki siapa pun.

**`contact_scope_axes`** — membatasi mereka pada agen, saluran, atau departemen tertentu:

| Kolom | Tipe | Deskripsi |
|---|---|---|
| `agents` | string[] | ID Agen. Mereka hanya melihat obrolan yang diarahkan ke salah satu agen ini. Maksimal 200. |
| `channels` | string[] | Nama saluran — `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `facebook`, `chat_widget`, `telegram`, `line`, `viber`, `tiktok`, `imessage`, `email`, `linkedin`, `skool`, `custom`, `custom_channel`. Maksimal 200. |
| `departments` | string[] | ID Departemen (lihat [Departemen](#departments)). Mereka hanya melihat prospek yang diajukan di bawah departemen tersebut. Maksimal 200. |
| `include_unrouted` | boolean | Dengan `agents` diatur, tampilkan juga obrolan yang tidak ditangani oleh agen mana pun. Nonaktif secara default. Diabaikan saat `agents` kosong. |
| `include_undepartmented` | boolean | Dengan `departments` diatur, tampilkan juga obrolan yang tidak berada di departemen mana pun. Nonaktif secara default. Diabaikan saat `departments` kosong. |

ID agen dan departemen tidak diperiksa saat Anda menyimpannya — ID yang tidak ada hanya tidak akan mencocokkan apa pun, yang akan muncul sebagai kotak masuk kosong alih-alih kesalahan. Nama saluran **diperiksa**: nama yang tidak dikenali akan ditolak dengan `400`.


Tidak satu pun dari ketiga hal ini dapat diatur pada pemilik akun — permintaan tersebut akan ditolak dengan `400`.

---

## Daftar undangan

`GET /team/invites`

Undangan yang telah Anda kirim, diurutkan dari yang terbaru, sehingga Anda dapat melihat siapa yang belum menerima undangan tersebut.

**Parameter kueri**

| Parameter | Wajib | Deskripsi |
|---|---|---|
| `status` | Tidak | Hanya mengembalikan undangan dalam status ini — `pending`, `accepted`, `declined`, `cancelled` atau `expired`. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/team/invites?status=pending" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Respons**

```json
{
  "success": true,
  "invites": [
    {
      "id": "inv_abc123",
      "account_owner_uid": "owner_uid_123",
      "account_owner_display_name": "Acme Ltd",
      "invitee_email": "sam@example.com",
      "invitee_uid": null,
      "role": "editor",
      "permission_overrides": [],
      "status": "pending",
      "created_by": "owner_uid_123",
      "created_at": "2026-06-10T12:00:00.000Z",
      "expires_at": "2026-06-17T12:00:00.000Z",
      "responded_at": null
    }
  ]
}
```

Token undangan tidak pernah dikembalikan — token tersebut hanya ada di dalam email yang dikirimkan.

---

## Mengirim undangan

`POST /team/invites`

Mengirim email undangan kepada seseorang untuk bergabung dengan tim Anda. Ini adalah cara umum untuk menambahkan rekan tim: mereka mengeklik tautan, masuk sebagai diri mereka sendiri, dan menerima undangan. Jika mereka belum memiliki akun <span data-t="appName">Your AI Connector</span>, akun akan dibuatkan untuk mereka dan email tersebut akan memandu mereka dalam mengatur kata sandi.

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `email` | Ya | Ke mana undangan harus dikirim. |
| `role` | Ya | `admin`, `editor` atau `viewer`. |
| `permission_overrides` | Tidak | Pengecualian per-area, diterapkan saat mereka menerima undangan. |
| `contact_scope` | Tidak | Diterapkan saat mereka menerima undangan. |
| `contact_scope_unassigned` | Tidak | Diterapkan saat mereka menerima undangan. |
| `contact_scope_axes` | Tidak | Diterapkan saat mereka menerima undangan. |
| `sub_account_access` | Tidak | Hanya untuk agensi. Diterapkan saat mereka menerima undangan. |

Mengatur izin di awal berarti Anda tidak perlu mengedit anggota setelahnya — semuanya disalin ke keanggotaan mereka saat mereka menerima undangan.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "email": "sam@example.com", "role": "editor" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/invites", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${idToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: "sam@example.com", role: "editor" }),
});
const { invite_id } = await res.json();
```

**Respons** — `201 Created`

```json
{
  "success": true,
  "invite_id": "inv_abc123",
  "message": "Team invite sent successfully."
}
```

**Hal-hal yang perlu direncanakan**

- **Undangan kedaluwarsa setelah 7 hari.** Undangan yang kedaluwarsa dapat dikirim ulang, yang akan memulai periode 7 hari yang baru.
- **Undangan yang tertunda menggunakan satu kursi.** Berbeda dengan menambahkan anggota secara langsung, pemeriksaan kursi di sini menghitung anggota aktif *ditambah* undangan yang tertunda, sehingga akun yang semua kursinya sudah terisi akan ditolak sebelum email dikirim.
- **20 undangan per hari**, dihitung per akun untuk pengiriman maupun pengiriman ulang.

| Status | Kapan |
|---|---|
| `400` | `email` tidak ada atau peran tidak valid. |
| `403` | Anda tidak memiliki izin untuk mengelola tim, atau Anda mencoba memberikan akses yang melebihi tingkat akses Anda sendiri. |
| `409` | Undangan tertunda untuk email tersebut sudah ada, atau orang tersebut sudah ada di tim Anda. |
| `429` | Kursi tim paket Anda sudah penuh, atau Anda telah mencapai batas 20 undangan per hari. Pesan `error` akan memberi tahu yang mana penyebabnya. |

---

## Membatalkan undangan

`DELETE /team/invites/{inviteId}`

Membatalkan undangan sebelum diterima. Tautan di dalam email tidak akan berfungsi lagi.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/team/invites/inv_abc123" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Respons**

```json
{
  "success": true,
  "message": "Team invite cancelled."
}
```

Undangan `pending` maupun `expired` dapat dibatalkan. Undangan yang sudah diterima, ditolak, atau dibatalkan akan mengembalikan `400`; undangan yang bukan milik Anda akan mengembalikan `403`; ID yang tidak dikenal akan mengembalikan `404`.

---

## Mengirim ulang undangan

`POST /team/invites/{inviteId}/resend`

Mengirim ulang email undangan — untuk kasus di mana email terlewat atau masuk ke spam. Berfungsi pada undangan `pending` dan `expired`, serta mengatur ulang masa berlaku menjadi 7 hari dari sekarang.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites/inv_abc123/resend" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Respons**

```json
{
  "success": true,
  "message": "Team invite resent successfully."
}
```

Email baru menyertakan tautan baru, dan **tautan lama tetap berfungsi**, sehingga orang yang menemukan email pertama di kemudian hari tidak akan terkendala. Mengirim ulang tetap dihitung dalam batas 20 undangan per hari seperti pengiriman biasa, dan mengaktifkan kembali undangan yang *kedaluwarsa* akan memeriksa ulang kuota kursi Anda — paket yang penuh akan ditolak dengan `429`.

---

## Menerima undangan

`POST /team/invites/accept`

Menerima undangan dengan token dari email undangan, yang akan memasukkan orang yang sedang masuk ke tim akun tersebut.

> **Ini adalah tindakan identitas Anda sendiri.** Masuklah sebagai diri sendiri — tindakan ini sengaja ditolak dengan `403` saat Anda sedang bekerja di dalam akun orang lain.

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `invite_token` | Ya | Token dari tautan email undangan. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites/accept" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "invite_token": "1f4c…" }'
```

**Respons**

```json
{
  "success": true,
  "team_member_id": "owner_uid_123_uid_sam",
  "account_owner_uid": "owner_uid_123",
  "message": "Team invite accepted successfully."
}
```

| Status | Kapan |
|---|---|
| `400` | `invite_token` tidak ada, atau undangan tersebut ditujukan untuk akun Anda sendiri. |
| `403` | Sesi sedang bekerja di dalam akun lain, atau undangan dikirim ke alamat email yang berbeda dengan yang Anda gunakan untuk masuk. |
| `404` | Undangan tidak ada atau sudah digunakan. |
| `429` | Kursi akun penuh antara waktu undangan dan penerimaan Anda. |
| `504` | Undangan telah kedaluwarsa. Minta pengirim untuk mengirimnya kembali. |

---

## Menolak undangan

`POST /team/invites/decline`

Menolak undangan dengan token dari email. Seperti halnya menerima, ini adalah tindakan identitas Anda sendiri dan akan ditolak saat Anda sedang bekerja di dalam akun lain.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites/decline" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "invite_token": "1f4c…" }'
```

**Respons**

```json
{
  "success": true,
  "message": "Team invite declined."
}
```

---

## Departemen

**Departemen** adalah grup bernama dari tim Anda — Penjualan, Dukungan pelanggan, SDM. Departemen memberikan tim pemilik kepada prospek, dapat mengklaim percakapan baru secara mandiri, dan dapat digunakan untuk membatasi apa yang dilihat oleh anggota.

> **Keempat endpoint ini memerlukan kunci API.** Berbeda dengan bagian lain di halaman ini, endpoint ini melakukan autentikasi seperti endpoint lainnya di API (lihat [Autentikasi](authentication.md)). Sesi yang masuk juga berfungsi: membaca memerlukan `contacts` di `view`, dan membuat, mengubah, atau menghapus memerlukan `team_management` di `edit`.

**Objek departemen**

| Bidang | Tipe | Deskripsi |
|---|---|---|
| `id` | string | ID departemen. Gunakan di `contact_scope_axes.departments` dan di jalur di bawah ini. |
| `name` | string | Nama tim. Maksimal 60 karakter, unik di akun. |
| `color` | string \| null | Warna aksen sebagai `#rrggbb`, atau `null`. |
| `member_uids` | string[] | Anggota tim di departemen ini. Dapat mencakup pemilik akun. |
| `auto_assign_enabled` | boolean | Apakah prospek yang dimasukkan ke departemen ini juga diserahkan kepada seseorang di dalamnya. `false` berarti departemen bekerja dari antrean bersama. |
| `routing_agents` | string[] | Percakapan baru yang ditangani oleh Agen AI ini secara otomatis dimasukkan ke departemen ini. Kosong berarti tidak ada aturan agen. |
| `routing_channels` | string[] | Percakapan baru di saluran ini secara otomatis dimasukkan ke sini. Kosong berarti tidak ada aturan saluran. |
| `created_by` | string \| null | Siapa yang membuatnya. |

Ketika `routing_agents` dan `routing_channels` keduanya diatur, percakapan harus cocok dengan **keduanya** agar dapat dimasukkan ke sini — begitulah cara Anda memberikan satu tim "agen dukungan, tetapi hanya di WhatsApp".

Satu akun dapat memiliki hingga **50** departemen.

### Daftar departemen

`GET /team/departments`

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

**Respons**

```json
{
  "success": true,
  "departments": [
    {
      "id": "dep_abc123",
      "name": "Sales",
      "color": "#2f6fed",
      "member_uids": ["uid_alice", "uid_bob"],
      "auto_assign_enabled": true,
      "routing_agents": [],
      "routing_channels": ["whatsapp"],
      "created_by": "owner_uid_123"
    }
  ]
}
```

### Membuat departemen

`POST /team/departments`

**Bidang permintaan**

| Bidang | Wajib | Deskripsi |
|---|---|---|
| `name` | Ya | Maksimal 60 karakter. Tidak boleh sama dengan departemen yang sudah ada. |
| `color` | Tidak | `#rrggbb` hex, atau `null`. |
| `member_uids` | Tidak | Siapa saja yang ada di dalamnya. Setiap UID harus merupakan pemilik akun atau anggota tim yang **aktif**. |
| `auto_assign_enabled` | Tidak | Default ke `true`. |
| `routing_agents` | Tidak | ID Agen yang obrolan barunya masuk ke sini. |
| `routing_channels` | Tidak | Nama saluran yang obrolan barunya masuk ke sini — kosakata yang sama dengan `contact_scope_axes.channels`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/departments?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice", "uid_bob"],
    "routing_channels": ["whatsapp"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/departments", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Sales",
    color: "#2f6fed",
    member_uids: ["uid_alice", "uid_bob"],
    routing_channels: ["whatsapp"],
  }),
});
const { department } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/team/departments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "Sales",
        "color": "#2f6fed",
        "member_uids": ["uid_alice", "uid_bob"],
        "routing_channels": ["whatsapp"],
    },
)
department = res.json()["department"]
```

**Respons** — `201 Created`

```json
{
  "success": true,
  "department": {
    "id": "dep_abc123",
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice", "uid_bob"],
    "auto_assign_enabled": true,
    "routing_agents": [],
    "routing_channels": ["whatsapp"],
    "created_by": "owner_uid_123"
  }
}
```

| Status | Kapan |
|---|---|
| `400` | `name` hilang atau terlalu panjang, `color` bukan `#rrggbb`, nama saluran tidak dikenali, UID yang terdaftar bukan anggota aktif tim ini, atau Anda sudah memiliki 50 departemen. |
| `409` | Departemen dengan nama tersebut sudah ada. |

### Memperbarui departemen

`PATCH /team/departments/{departmentId}`

Mengubah departemen. Hanya bidang yang Anda kirim yang akan diubah.

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/team/departments/dep_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "member_uids": ["uid_alice"], "auto_assign_enabled": false }'
```

**Respons**

```json
{
  "success": true,
  "department": {
    "id": "dep_abc123",
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice"],
    "auto_assign_enabled": false,
    "routing_agents": [],
    "routing_channels": ["whatsapp"],
    "created_by": "owner_uid_123"
  }
}
```

Mengirim kolom yang tidak dikenali akan mengembalikan `400`; departemen yang tidak diketahui akan mengembalikan `404`; nama yang bentrok dengan departemen lain akan mengembalikan `409`.

### Menghapus departemen

`DELETE /team/departments/{departmentId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/team/departments/dep_abc123?apiKey=YOUR_API_KEY"
```

**Respons**

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

> **Menghapus departemen yang membatasi akses seseorang akan ditolak.** Respons `400` menyebutkan anggota yang visibilitasnya dibatasi pada departemen tersebut, sehingga Anda dapat mengubah cakupannya terlebih dahulu. Hal ini disengaja: menghapus batasan secara diam-diam akan memberikan mereka akses ke seluruh basis pelanggan Anda tanpa ada pemberitahuan bahwa hal itu terjadi.

Kontak yang tersimpan di bawah departemen yang dihapus tidak akan diubah — mereka hanya akan berhenti menampilkan departemen, dan saat Anda menyimpannya kembali, departemen tersebut akan terpasang.

---

## Periksa izin Anda sendiri

`GET /team/permissions`

Mengembalikan apa yang diizinkan untuk dilakukan oleh orang yang masuk di akun tempat mereka bekerja saat ini. Gunakan ini untuk menyembunyikan tombol yang tidak dapat digunakan oleh anggota, alih-alih membiarkan mereka mengetahui batasan tersebut melalui pesan kesalahan.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/team/permissions" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Respons — pemilik akun**

```json
{
  "success": true,
  "role": "owner",
  "is_team_mode": false,
  "permissions": {
    "campaigns": "full",
    "contacts": "full",
    "messages": "full",
    "appointments": "full",
    "settings": "full",
    "billing": "full",
    "team_management": "full",
    "analytics": "full",
    "phone_numbers": "full",
    "integrations": "full",
    "faqs": "full",
    "daily_summaries": "full"
  }
}
```

**Respons — anggota tim yang bekerja di dalam akun**

```json
{
  "success": true,
  "role": "editor",
  "is_team_mode": true,
  "permissions": { "campaigns": "edit", "billing": "none", "…": "…" },
  "member": {
    "uid": "uid_sam",
    "email": "sam@example.com",
    "display_name": "Sam Rivera",
    "account_owner_uid": "owner_uid_123"
  }
}
```

`role` adalah `owner` ketika orang yang masuk adalah pemilik akun; jika tidak, itu adalah peran tim mereka. `member` hanya ada dalam mode tim, dan memuat `contact_scope`, `contact_scope_unassigned`, dan `contact_scope_axes` ketika keanggotaan mereka memilikinya.

---

## Token sesi

Lima endpoint membuat token masuk sekali pakai untuk berpindah antar akun. Semuanya memberikan respons yang sama:

```json
{
  "success": true,
  "customToken": "eyJhbGciOi…"
}
```

Token tersebut ditukarkan dengan sesi menggunakan SDK klien Firebase. **Ini bukan kunci API dan tidak dapat dikirim sebagai kunci API**, itulah sebabnya endpoint ini hanya berguna di dalam aplikasi pihak pertama.

| Endpoint | Fungsi | Isi |
|---|---|---|
| `POST /team/tokens/team-member` | Memungkinkan anggota tim mulai bekerja di dalam akun tempat mereka berada. | `account_owner_uid` (wajib) |
| `POST /team/tokens/return-from-team` | Membawa mereka kembali keluar, ke akun mereka sendiri. | — |
| `POST /team/tokens/assist` | Memungkinkan staf <span data-t="appName">Your AI Connector</span> membuka akun pelanggan untuk membantu. Hanya untuk staf. | `customerUid` |
| `POST /team/tokens/return-to-admin` | Mengakhiri sesi bantuan dan mengembalikan staf ke akun mereka sendiri. | — |
| `POST /team/tokens/agency-assist` | Memungkinkan agensi membuka salah satu sub-akun kliennya — atau, jika dipanggil tanpa sub-akun, kembali ke akun agensi. | `subAccountUid` (opsional) |

Masing-masing menolak dengan `403` jika sesi tidak berhak atasnya: bukan anggota akun tersebut, bukan staf, sub-akun tersebut tidak ada di agensi Anda atau belum diberikan kepada Anda, atau sesi saat ini tidak dalam mode yang diakhiri oleh endpoint.

---

## Menetapkan peran platform

`POST /team/users/{targetUid}/role`

Menetapkan peran **platform** pengguna — `User`, `Dev`, `Support`, atau `Agency`. Ini bukan keanggotaan tim: ini adalah jenis akun <span data-t="appName">Your AI Connector</span> yang dimiliki seseorang.

Endpoint ini dibatasi untuk staf <span data-t="appName">Your AI Connector</span>, dan `Dev` terakhir yang tersisa tidak dapat diturunkan pangkatnya. Dicantumkan untuk kelengkapan; ini bukan bagian dari pengelolaan tim Anda sendiri.

```json
{
  "success": true,
  "targetUid": "uid_sam",
  "role": "Agency",
  "claimUpdated": true
}
```

| Status | Kapan |
|---|---|
| `400` | `role` tidak ada atau bukan salah satu dari empat peran, atau ini akan menghapus `Dev` terakhir. |
| `403` | Anda bukan staf, atau sesi sedang bekerja di dalam akun lain. |
| `404` | Pengguna tidak ditemukan. |

---

## Kesalahan API Tim

Endpoint tim mengembalikan amplop kesalahan standar, selalu dengan `error_code` di samping status HTTP:

```json
{
  "success": false,
  "error_code": 403,
  "error": "Cannot grant \"full\" access to \"billing\" — exceeds your own permissions."
}
```

| Status | Kapan terjadi pada endpoint tim |
|---|---|
| `400` | Bidang yang diperlukan tidak ada atau tidak valid, atau tindakan tidak diizinkan dalam status ini (mengaktifkan kembali anggota yang dihapus, menangguhkan pemilik, menghapus departemen yang dibatasi untuk seseorang). |
| `401` | Anda mengirim kunci API ke endpoint yang memerlukan orang yang masuk — lihat [Autentikasi](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | Anda tidak memiliki izin `team_management`, perubahan melebihi akses Anda sendiri, atau tindakan ditolak saat bekerja di dalam akun lain. |
| `404` | Tidak ada anggota, undangan, departemen, atau pengguna tersebut. |
| `409` | Sudah menjadi anggota tim, undangan tertunda sudah ada, atau departemen dengan nama tersebut sudah ada. |
| `429` | Kursi tim penuh, batas 20 undangan per hari tercapai, atau Anda mencapai batas kecepatan API. |
| `504` | Undangan yang Anda coba terima telah kedaluwarsa. |

Kode bersama yang dapat dikembalikan oleh setiap endpoint — `429` (batas kecepatan) dan `500` — tercantum dengan panduan percobaan ulang di [Kesalahan & Penomoran Halaman](errors-and-pagination.md).

---

## Terkait

- [Manajemen Tim](../settings/team-management.md) — fitur yang sama di dasbor, dengan tangkapan layar.
- [Autentikasi](authentication.md) — cara mengirim token ID Firebase alih-alih kunci API.
- [API Kontak](contacts.md) — kontak yang menjadi batasan visibilitas anggota.

