
# API Đội ngũ

Đội ngũ của bạn bao gồm tất cả những người làm việc trong tài khoản của bạn ngoài bạn ra — quản trị viên, nhân viên hỗ trợ và người xem chỉ đọc — cộng với các lời mời bạn đã gửi và các phòng ban mà bạn tổ chức họ vào đó. API Đội ngũ là phiên bản lập trình của **Cài đặt → Đội ngũ**: thêm và xóa người, thiết lập những gì mỗi người trong số họ có thể xem và làm, gửi và nhắc nhở các lời mời, và quản lý các phòng ban.

Tất cả các điểm cuối bên dưới đều tương đối với URL cơ sở `https://api.youraiconnector.com/v1`. Để biết phiên bản bảng điều khiển của mọi thứ trên trang này, hãy xem [Quản lý Đội ngũ](../settings/team-management.md).

---

## Xác thực: các điểm cuối này yêu cầu một người đã đăng nhập

**Đây là phần duy nhất của API mà khóa API không thể sử dụng.** Mọi điểm cuối `/team` ngoại trừ các điểm cuối [phòng ban](#departments) đều phải được gọi bằng **mã thông báo Firebase ID** từ một phiên đăng nhập:

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

Nếu gửi khóa API thay thế, yêu cầu sẽ bị từ chối với mã `401`:

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

Lý do là các điểm cuối này quyết định phải làm gì dựa trên **người đang đăng nhập**: vai trò của bạn, giới hạn về những gì bạn được phép cấp cho người khác và việc bạn có đang làm việc trong một tài khoản khác hay không. Khóa API là một tích hợp, không phải là một con người, vì vậy không có ai để áp dụng các quy tắc đó.

Trên thực tế, điều đó có nghĩa là API Đội ngũ dành cho ứng dụng bên thứ nhất với người dùng <span data-t="appName">Your AI Connector</span> đã đăng nhập (xem [Xác thực → Mã thông báo Firebase ID](authentication.md#4-firebase-id-token-first-party-only)). Một tích hợp máy chủ với máy chủ không thể quản lý các thành viên trong đội ngũ — không có cách nào để tạo ra một trong những mã thông báo này từ bên ngoài ứng dụng.

> **Ngoại lệ:** bốn điểm cuối [phòng ban](#departments) là các điểm cuối API thông thường. Chúng chấp nhận khóa API của bạn giống như phần còn lại của API, cũng như một phiên đăng nhập.

Mọi phản hồi trên trang này đều tuân theo phong bì thông thường: `success: true` cộng với các trường của điểm cuối ở cấp cao nhất, hoặc `success: false` với `error` và `error_code` khi có sự cố xảy ra.

---

## Vai trò và quyền hạn

Mỗi thành viên trong đội ngũ có một **vai trò**, thiết lập quyền truy cập mặc định của họ trên 12 khu vực của ứng dụng. Sau đó, bạn có thể ghi đè các khu vực riêng lẻ.

| Vai trò | Giá trị | Tóm tắt |
|---|---|---|
| Quản trị viên | `admin` | Mọi thứ ngoại trừ các hành động cấp thanh toán của chủ sở hữu. |
| Biên tập viên | `editor` | Có thể tạo và thay đổi mọi thứ. Được hiển thị là **Nhân viên hỗ trợ** trong ứng dụng. |
| Người xem | `viewer` | Chỉ đọc. |

Mỗi khu vực được đặt thành một trong bốn cấp độ: `none` (ẩn), `view` (chỉ đọc), `edit` (tạo và thay đổi), `full` (bao gồm cả xóa).

| Khu vực | Quản trị viên | Biên tập viên | Người xem |
|---|---|---|---|
| `campaigns` | đầy đủ | chỉnh sửa | xem |
| `contacts` | đầy đủ | chỉnh sửa | xem |
| `messages` | đầy đủ | chỉnh sửa | xem |
| `appointments` | đầy đủ | chỉnh sửa | xem |
| `settings` | chỉnh sửa | xem | không |
| `billing` | chỉnh sửa | không | không |
| `team_management` | chỉnh sửa | không | không |
| `analytics` | đầy đủ | xem | xem |
| `phone_numbers` | chỉnh sửa | không | không |
| `integrations` | chỉnh sửa | không | không |
| `faqs` | đầy đủ | chỉnh sửa | xem |
| `daily_summaries` | đầy đủ | xem | xem |

Để thay đổi các mặc định của vai trò, hãy gửi `permission_overrides` — một mảng các đối tượng `{ "area": ..., "level": ... }`. Mỗi mục sẽ thay thế mặc định của vai trò cho khu vực đó; mọi thứ bạn không liệt kê sẽ giữ nguyên mặc định của vai trò.

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

**Ai có thể gọi các endpoint này**

- **Chủ tài khoản** luôn có thể thực hiện mọi thao tác.
- Một thành viên trong nhóm cần `team_management` tại `view` để đọc danh sách thành viên và danh sách lời mời, và tại `edit` để thêm, thay đổi, tạm dừng, xóa, mời, hủy hoặc gửi lại lời mời. Quản trị viên có `edit` theo mặc định; biên tập viên và người xem có `none`, vì vậy theo mặc định chỉ quản trị viên mới có thể quản lý nhóm.
- **Không ai có thể cấp quyền cao hơn quyền của chính họ.** Nếu bạn cố gắng cấp cho ai đó một cấp độ mà bạn không có — hoặc chỉnh sửa, tạm dừng hay xóa một người có quyền truy cập rộng hơn bạn — yêu cầu sẽ bị từ chối với `403` cùng một thông báo nêu rõ khu vực đó.

---

## Đối tượng thành viên nhóm

`GET /team/members` trả về một trong các đối tượng này cho mỗi thành viên:

| Trường | Kiểu | Mô tả |
|---|---|---|
| `member_uid` | string | ID người dùng của chính thành viên đó. Đây là `{memberUid}` trong các đường dẫn bên dưới. |
| `account_owner_uid` | string | Tài khoản mà họ là thành viên. |
| `member_email` | string | Địa chỉ email của họ. |
| `member_display_name` | string | Tên hiển thị của họ trong ứng dụng. |
| `role` | string | `admin`, `editor` hoặc `viewer`. |
| `permission_overrides` | array | Các ngoại lệ theo từng khu vực của họ. `[]` khi họ hoàn toàn sử dụng các mặc định của vai trò. |
| `status` | string | `active` hoặc `suspended`. |
| `auto_assign_enabled` | boolean \| null | Liệu các liên hệ mới có thể được tự động gán cho họ hay không. `null` nghĩa là chưa bao giờ thay đổi, hoạt động như `true`. |
| `created_by` | string | Người đã thêm họ. |
| `created_at` | string \| null | Dấu thời gian ISO 8601. |
| `updated_at` | string \| null | Dấu thời gian ISO 8601. |

Các thành viên đã xóa sẽ không được trả về — danh sách này chỉ bao gồm các thành viên đang hoạt động và bị tạm dừng.

> **Các giới hạn hiển thị chỉ có thể ghi ở đây.** `contact_scope`, `contact_scope_axes` và `sub_account_access` (xem [Giới hạn những gì thành viên có thể thấy](#limiting-what-a-member-can-see)) có thể được thiết lập khi tạo, cập nhật và mời, nhưng endpoint này không trả về chúng.

---

## Liệt kê các thành viên nhóm

`GET /team/members`

Trả về danh sách thành viên cùng với số lượng chỗ ngồi trong gói của bạn, để bạn có thể hiển thị "3 trên 5 chỗ ngồi" và biết khi nào việc mời thành viên sắp bị từ chối.

**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()
```

**Phản hồi**

```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` là `null` khi gói của bạn không có giới hạn chỗ ngồi. `seats_used` chỉ đếm các thành viên **đang hoạt động** — việc tạm dừng hoặc xóa một người sẽ giải phóng chỗ ngồi của họ ngay lập tức.

---

## Thêm trực tiếp một thành viên nhóm

`POST /team/members`

Đưa một người vào nhóm của bạn ngay lập tức mà không cần lời mời.

> **Thao tác này không gửi email.** Không ai được thông báo rằng họ đã được thêm vào, và nếu họ chưa có thông tin đăng nhập <span data-t="appName">Your AI Connector</span>, tài khoản được tạo cho họ sẽ **không có mật khẩu**, vì vậy họ không thể đăng nhập cho đến khi đặt lại mật khẩu. Hãy sử dụng [Gửi lời mời](#send-an-invitation) trừ khi bạn có cách riêng để thông báo cho người đó và giúp họ đăng nhập.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `email` | Có | Địa chỉ email của thành viên trong nhóm. |
| `display_name` | Có | Tên hiển thị của họ trong ứng dụng. |
| `role` | Có | `admin`, `editor` hoặc `viewer`. |
| `permission_overrides` | Không | Các ngoại lệ theo từng khu vực so với mặc định của vai trò. |
| `contact_scope` | Không | `all` hoặc `assigned` — xem [Giới hạn những gì thành viên có thể thấy](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | Không | Với `assigned`, cũng cho phép họ xem các liên hệ chưa được sở hữu bởi ai. |
| `contact_scope_axes` | Không | Giới hạn họ với các đại lý, kênh hoặc phòng ban được chỉ định. |
| `sub_account_access` | Không | Chỉ dành cho đại lý — các tài khoản phụ khách hàng mà họ có thể mở. |

**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();
```

**Phản hồi** — `201 Created`

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

| Trạng thái | Khi nào |
|---|---|
| `400` | `email`, `display_name` hoặc `role` bị thiếu, vai trò không thuộc một trong ba loại trên, hoặc bạn đã cố gắng thêm chính mình. |
| `403` | Bạn không có quyền quản lý nhóm, hoặc bạn đã cố gắng cấp quyền truy cập cao hơn quyền của chính bạn. |
| `409` | Người đó đã là thành viên đang hoạt động trong nhóm của bạn. |
| `429` | Số lượng chỗ ngồi trong nhóm theo gói của bạn đã đầy. |

Việc thêm một người trước đây đã bị **đình chỉ hoặc xóa** sẽ khôi phục trạng thái của họ thay vì thất bại.

---

## Cập nhật thành viên nhóm

`PATCH /team/members/{memberUid}`

Thay đổi vai trò, quyền hạn, khả năng hiển thị, quyền truy cập khách hàng của thành viên, hoặc việc họ có tham gia vào quá trình phân bổ liên hệ tự động hay không. Chỉ gửi các trường bạn muốn thay đổi; bất kỳ trường nào bạn bỏ qua sẽ giữ nguyên giá trị hiện tại.

**Các trường yêu cầu**

| Trường | Mô tả |
|---|---|
| `role` | `admin`, `editor` hoặc `viewer`. |
| `permission_overrides` | Thay thế toàn bộ danh sách ghi đè của họ. Gửi `[]` để đưa họ trở lại các giá trị mặc định thuần túy của vai trò. |
| `status` | Chỉ chấp nhận `active` để đưa một thành viên bị đình chỉ trở lại. Để đình chỉ ai đó, hãy sử dụng [endpoint đình chỉ](#suspend-a-team-member). |
| `auto_assign_enabled` | `true` hoặc `false`. |
| `contact_scope` | `all` hoặc `assigned`. |
| `contact_scope_unassigned` | `true` hoặc `false`. |
| `contact_scope_axes` | Xem [Giới hạn những gì thành viên có thể thấy](#limiting-what-a-member-can-see). |
| `sub_account_access` | Chỉ dành cho đại lý. |

> **Đây là endpoint duy nhất mà `null` có nghĩa là "xóa".** Gửi `"contact_scope": null`, `"contact_scope_axes": null` hoặc `"sub_account_access": null` sẽ xóa hoàn toàn giới hạn đó và đưa thành viên trở lại trạng thái nhìn thấy mọi thứ. Khi tạo và mời, `null` đơn giản có nghĩa là "không được cung cấp".

**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" }]
  }'
```

**Phản hồi**

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

| Trạng thái | Khi nào |
|---|---|
| `400` | Giá trị `status` hoặc `auto_assign_enabled` không hợp lệ, hoặc bạn đã cố gắng kích hoạt lại một thành viên đã bị xóa (thành viên bị xóa phải được mời lại). |
| `403` | Bạn không có quyền, hoặc thay đổi này sẽ chỉnh sửa hoặc tạo quyền truy cập rộng hơn quyền của bạn. |
| `404` | Không tìm thấy thành viên nhóm đó. |

---

## Đình chỉ thành viên nhóm

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

Đình chỉ một người: họ vẫn giữ vị trí trong nhóm nhưng mất quyền truy cập. Hãy sử dụng cách này thay vì xóa khi việc tạm dừng chỉ là tạm thời — đưa họ trở lại với `PATCH /team/members/{memberUid}` và `{"status": "active"}`.

**cURL**

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

**Phản hồi**

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

Một thành viên bị đình chỉ sẽ **giải phóng chỗ ngồi của họ**, vì vậy bạn có thể mời người khác thay thế. Quyền truy cập của họ sẽ kết thúc khi token phiên hiện tại của họ làm mới lần tiếp theo, có thể mất đến một giờ — hãy xóa họ nếu bạn cần việc này diễn ra ngay lập tức.

| Trạng thái | Khi nào |
|---|---|
| `400` | Bạn đã cố gắng đình chỉ chủ sở hữu tài khoản, hoặc một thành viên đã bị đình chỉ hoặc xóa. |
| `403` | Quyền truy cập của họ rộng hơn quyền của bạn. |
| `404` | Không tìm thấy thành viên nhóm đó. |

---

## Xóa thành viên nhóm

`DELETE /team/members/{memberUid}`

Xóa một người khỏi nhóm của bạn và giải phóng chỗ ngồi của họ. Họ sẽ bị đăng xuất và mất quyền truy cập vào tài khoản của bạn; thông tin đăng nhập của riêng họ vẫn không bị ảnh hưởng.

**cURL**

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

**Phản hồi**

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

Việc xóa là vĩnh viễn từ phía bạn: thành viên đã bị xóa **không thể được kích hoạt lại** bằng endpoint cập nhật — hãy mời lại họ nếu bạn đổi ý. Email của họ cũng bị xóa khỏi danh sách thông báo của tài khoản bạn.

| Trạng thái | Khi nào |
|---|---|
| `400` | Bạn đã cố gắng xóa chủ sở hữu tài khoản. |
| `403` | Quyền truy cập của họ rộng hơn của bạn. |
| `404` | Không tồn tại thành viên nhóm này. |

---

## Giới hạn những gì thành viên có thể xem

Ba trường tùy chọn, được chấp nhận khi [thêm](#add-a-team-member-directly), [cập nhật](#update-a-team-member) và [mời](#send-an-invitation), quyết định mức độ truy cập của một người vào tài khoản. Chúng được cộng dồn: một thành viên bị giới hạn ở nhiều hơn một mục sẽ bị giới hạn bởi tất cả các mục đó.

**`contact_scope`** — `all` (mặc định: mọi liên hệ và cuộc hội thoại) hoặc `assigned` (chỉ những mục được chỉ định cho họ). Với `assigned`, hãy thêm `"contact_scope_unassigned": true` để cho phép họ xem cả các liên hệ chưa có chủ sở hữu.

**`contact_scope_axes`** — giới hạn họ với các tác nhân, kênh hoặc phòng ban được chỉ định:

| Trường | Loại | Mô tả |
|---|---|---|
| `agents` | string[] | ID tác nhân. Họ chỉ thấy các cuộc trò chuyện được định tuyến đến một trong các tác nhân này. Tối đa 200. |
| `channels` | string[] | Tên kênh — `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `facebook`, `chat_widget`, `telegram`, `line`, `viber`, `tiktok`, `imessage`, `email`, `linkedin`, `skool`, `custom`, `custom_channel`. Tối đa 200. |
| `departments` | string[] | ID phòng ban (xem [Phòng ban](#departments)). Họ chỉ thấy các khách hàng tiềm năng được lưu dưới các phòng ban này. Tối đa 200. |
| `include_unrouted` | boolean | Với `agents` được đặt, cũng hiển thị các cuộc trò chuyện không có tác nhân nào xử lý. Mặc định là Tắt. Bị bỏ qua khi `agents` trống. |
| `include_undepartmented` | boolean | Với `departments` được đặt, cũng hiển thị các cuộc trò chuyện không thuộc phòng ban nào. Mặc định là Tắt. Bị bỏ qua khi `departments` trống. |

ID tác nhân và phòng ban không được kiểm tra khi bạn lưu chúng — một ID không tồn tại đơn giản là không khớp với bất kỳ thứ gì, điều này sẽ hiển thị dưới dạng hộp thư đến trống thay vì lỗi. Tên kênh **được** kiểm tra: một tên không được nhận dạng sẽ bị từ chối với `400`.


Không trường nào trong ba trường này có thể được đặt cho chủ sở hữu tài khoản — yêu cầu đó sẽ bị từ chối với `400`.

---

## Liệt kê lời mời

`GET /team/invites`

Các lời mời bạn đã gửi, hiển thị theo thứ tự mới nhất trước, để bạn có thể xem ai chưa chấp nhận lời mời.

**Các tham số truy vấn**

| Tham số | Bắt buộc | Mô tả |
|---|---|---|
| `status` | Không | Chỉ trả về các lời mời ở trạng thái này — `pending`, `accepted`, `declined`, `cancelled` hoặc `expired`. |

**cURL**

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

**Phản hồi**

```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 lời mời không bao giờ được trả về — nó chỉ tồn tại trong email đã được gửi đi.

---

## Gửi lời mời

`POST /team/invites`

Gửi email lời mời tham gia nhóm của bạn cho một người nào đó. Đây là cách thông thường để thêm một thành viên vào nhóm: họ nhấp vào liên kết, đăng nhập bằng tài khoản của chính họ và chấp nhận. Nếu họ chưa có tài khoản <span data-t="appName">Your AI Connector</span>, một tài khoản sẽ được tạo cho họ và email sẽ hướng dẫn họ cách thiết lập mật khẩu.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `email` | Có | Địa chỉ gửi lời mời đến. |
| `role` | Có | `admin`, `editor` hoặc `viewer`. |
| `permission_overrides` | Không | Các ngoại lệ theo từng khu vực, được áp dụng ngay khi họ chấp nhận. |
| `contact_scope` | Không | Được áp dụng khi họ chấp nhận. |
| `contact_scope_unassigned` | Không | Được áp dụng khi họ chấp nhận. |
| `contact_scope_axes` | Không | Được áp dụng khi họ chấp nhận. |
| `sub_account_access` | Không | Chỉ dành cho các đại lý. Được áp dụng khi họ chấp nhận. |

Việc thiết lập quyền ngay từ đầu có nghĩa là bạn không cần phải chỉnh sửa thành viên sau đó — mọi thứ sẽ được sao chép vào tư cách thành viên của họ khi họ chấp nhận.

**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();
```

**Phản hồi** — `201 Created`

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

**Những điều cần lưu ý**

- **Lời mời sẽ hết hạn sau 7 ngày.** Lời mời đã hết hạn có thể được gửi lại, bắt đầu một khoảng thời gian 7 ngày mới.
- **Lời mời đang chờ xử lý sẽ chiếm một chỗ.** Không giống như việc thêm thành viên trực tiếp, kiểm tra chỗ ngồi ở đây tính cả các thành viên đang hoạt động *cộng với* các lời mời đang chờ xử lý, vì vậy một tài khoản đã hết chỗ sẽ bị từ chối trước khi email được gửi đi.
- **20 lời mời mỗi ngày**, được tính trên mỗi tài khoản cho cả việc gửi mới và gửi lại.

| Trạng thái | Khi nào |
|---|---|
| `400` | `email` bị thiếu hoặc vai trò không hợp lệ. |
| `403` | Bạn không có quyền quản lý nhóm, hoặc bạn đã cố gắng cấp quyền cao hơn quyền của chính bạn. |
| `409` | Một lời mời đang chờ xử lý cho email đó đã tồn tại, hoặc người đó đã có trong nhóm của bạn. |
| `429` | Số chỗ ngồi trong nhóm của gói dịch vụ đã đầy, hoặc bạn đã đạt giới hạn 20 lời mời mỗi ngày. Thông báo `error` sẽ cho biết lý do cụ thể. |

---

## Hủy lời mời

`DELETE /team/invites/{inviteId}`

Hủy lời mời trước khi nó được chấp nhận. Liên kết trong email sẽ ngừng hoạt động.

**cURL**

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

**Phản hồi**

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

Cả lời mời `pending` và `expired` đều có thể bị hủy. Lời mời đã được chấp nhận, từ chối hoặc hủy bỏ sẽ trả về `400`; lời mời không phải của bạn sẽ trả về `403`; ID không xác định sẽ trả về `404`.

---

## Gửi lại lời mời

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

Gửi lại email mời — dùng khi người nhận bỏ lỡ hoặc email rơi vào thư mục spam. Hoạt động với lời mời `pending` và `expired`, đồng thời đặt lại thời hạn hết hiệu lực thành 7 ngày kể từ bây giờ.

**cURL**

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

**Phản hồi**

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

Email mới chứa một liên kết mới, và **liên kết cũ vẫn tiếp tục hoạt động**, vì vậy người tìm thấy email đầu tiên sau đó sẽ không bị chặn. Việc gửi lại được tính vào giới hạn 20 lần mỗi ngày giống như việc gửi mới, và việc khôi phục một lời mời *đã hết hạn* sẽ kiểm tra lại số chỗ ngồi của bạn — gói dịch vụ đầy sẽ bị từ chối với `429`.

---

## Chấp nhận lời mời

`POST /team/invites/accept`

Chấp nhận lời mời bằng mã thông báo từ email mời, thêm người đã đăng nhập vào nhóm của tài khoản đó.

> **Đây là hành động xác thực danh tính của chính bạn.** Hãy đăng nhập với tư cách là chính bạn — hành động này sẽ bị từ chối một cách có chủ đích với `403` khi bạn đang làm việc bên trong tài khoản của người khác.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `invite_token` | Có | Mã thông báo từ liên kết trong email mời. |

**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…" }'
```

**Phản hồi**

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

| Trạng thái | Khi nào |
|---|---|
| `400` | `invite_token` bị thiếu hoặc lời mời dành cho chính tài khoản của bạn. |
| `403` | Phiên làm việc đang diễn ra trong một tài khoản khác, hoặc lời mời được gửi đến một địa chỉ email khác với địa chỉ bạn đang đăng nhập. |
| `404` | Lời mời không tồn tại hoặc đã được sử dụng. |
| `429` | Số chỗ ngồi của tài khoản đã đầy trong khoảng thời gian từ khi gửi lời mời đến khi bạn chấp nhận. |
| `504` | Lời mời đã hết hạn. Hãy yêu cầu người gửi gửi lại. |

---

## Từ chối lời mời

`POST /team/invites/decline`

Từ chối lời mời bằng mã thông báo từ email. Giống như việc chấp nhận, đây là hành động xác thực danh tính của chính bạn và sẽ bị từ chối khi bạn đang làm việc bên trong một tài khoản khác.

**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…" }'
```

**Phản hồi**

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

---

## Phòng ban

**Phòng ban** là một nhóm được đặt tên trong đội ngũ của bạn — Ví dụ: Kinh doanh, Hỗ trợ khách hàng, Nhân sự. Nó cung cấp cho một khách hàng tiềm năng một nhóm sở hữu, có thể tự nhận các cuộc hội thoại mới và có thể được sử dụng để giới hạn những gì một thành viên có thể nhìn thấy.

> **Bốn endpoint này yêu cầu khóa API.** Không giống như phần còn lại của trang này, chúng xác thực giống như mọi endpoint khác trong API (xem [Xác thực](authentication.md)). Một phiên đăng nhập cũng hoạt động: việc đọc cần `contacts` tại `view`, và việc tạo, thay đổi hoặc xóa cần `team_management` tại `edit`.

**Đối tượng phòng ban**

| Trường | Loại | Mô tả |
|---|---|---|
| `id` | string | ID của phòng ban. Sử dụng nó trong `contact_scope_axes.departments` và trong các đường dẫn bên dưới. |
| `name` | string | Tên của nhóm. Tối đa 60 ký tự, là duy nhất trong tài khoản. |
| `color` | string \| null | Màu nhấn dưới dạng `#rrggbb`, hoặc `null`. |
| `member_uids` | string[] | Các thành viên trong phòng ban này. Có thể bao gồm cả chủ sở hữu tài khoản. |
| `auto_assign_enabled` | boolean | Liệu một khách hàng tiềm năng được phân loại vào phòng ban này có được chuyển cho ai đó trong phòng ban hay không. `false` nghĩa là phòng ban hoạt động từ một hàng đợi chung. |
| `routing_agents` | string[] | Các cuộc hội thoại mới do các AI Agent này xử lý sẽ tự động được phân loại vào phòng ban này. Để trống nghĩa là không có quy tắc đại lý. |
| `routing_channels` | string[] | Các cuộc hội thoại mới trên các kênh này sẽ tự động được phân loại vào đây. Để trống nghĩa là không có quy tắc kênh. |
| `created_by` | string \| null | Người đã tạo ra nó. |

Khi cả `routing_agents` và `routing_channels` đều được thiết lập, một cuộc hội thoại phải khớp với **cả hai** để được phân loại vào đây — đó là cách bạn giao cho một nhóm "đại lý hỗ trợ, nhưng chỉ trên WhatsApp".

Một tài khoản có thể có tối đa **50** phòng ban.

### Liệt kê các phòng ban

`GET /team/departments`

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

**Phản hồi**

```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"
    }
  ]
}
```

### Tạo một phòng ban

`POST /team/departments`

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `name` | Có | Tối đa 60 ký tự. Không được trùng với phòng ban hiện có. |
| `color` | Không | `#rrggbb` hex, hoặc `null`. |
| `member_uids` | Không | Ai tham gia. Mọi UID phải là chủ sở hữu tài khoản hoặc thành viên nhóm **đang hoạt động**. |
| `auto_assign_enabled` | Không | Mặc định là `true`. |
| `routing_agents` | Không | ID của các tác nhân có cuộc trò chuyện mới tại đây. |
| `routing_channels` | Không | Tên kênh có cuộc trò chuyện mới tại đây — cùng từ vựng với `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"]
```

**Phản hồi** — `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"
  }
}
```

| Trạng thái | Khi nào |
|---|---|
| `400` | `name` bị thiếu hoặc quá dài, `color` không phải là `#rrggbb`, tên kênh không được nhận dạng, UID được liệt kê không phải là thành viên đang hoạt động của nhóm này, hoặc bạn đã có 50 phòng ban. |
| `409` | Một phòng ban với tên đó đã tồn tại. |

### Cập nhật một phòng ban

`PATCH /team/departments/{departmentId}`

Thay đổi một phòng ban. Chỉ các trường bạn gửi mới bị thay đổi.

```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 }'
```

**Phản hồi**

```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"
  }
}
```

Việc không gửi trường nào được nhận diện sẽ trả về `400`; một phòng ban không xác định sẽ trả về `404`; một tên trùng với phòng ban khác sẽ trả về `409`.

### Xóa phòng ban

`DELETE /team/departments/{departmentId}`

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

**Phản hồi**

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

> **Việc xóa một phòng ban mà ai đó bị giới hạn quyền truy cập sẽ bị từ chối.** Phản hồi `400` sẽ nêu tên các thành viên có phạm vi hiển thị bị thu hẹp trong phòng ban đó, để bạn có thể thay đổi phạm vi của họ trước. Điều này là có chủ đích: việc âm thầm gỡ bỏ giới hạn của họ sẽ khiến họ có quyền truy cập vào toàn bộ cơ sở khách hàng của bạn mà không có bất kỳ dấu hiệu nào cho thấy điều đó đã xảy ra.

Các liên hệ được lưu trữ trong một phòng ban đã bị xóa sẽ không được viết lại — chúng chỉ đơn giản là ngừng hiển thị phòng ban, và lần tiếp theo bạn lưu trữ chúng, thông tin đó sẽ được cập nhật.

---

## Kiểm tra quyền hạn của chính bạn

`GET /team/permissions`

Trả về những gì người đã đăng nhập được phép thực hiện trong tài khoản mà họ hiện đang làm việc. Hãy sử dụng nó để ẩn các nút mà thành viên không thể sử dụng, thay vì để họ tự phát hiện giới hạn thông qua thông báo lỗi.

**cURL**

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

**Phản hồi — chủ sở hữu tài khoản**

```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"
  }
}
```

**Phản hồi — một thành viên trong nhóm đang làm việc bên trong tài khoản**

```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` là `owner` khi người đã đăng nhập là chủ sở hữu tài khoản; nếu không, đó là vai trò trong nhóm của họ. `member` chỉ xuất hiện ở chế độ nhóm, và mang theo `contact_scope`, `contact_scope_unassigned` và `contact_scope_axes` khi tư cách thành viên của họ có các quyền này.

---

## Token phiên

Năm endpoint tạo ra một token đăng nhập dùng một lần để chuyển đổi giữa các tài khoản. Tất cả chúng đều phản hồi theo cùng một cách:

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

Token này được đổi lấy một phiên làm việc với Firebase client SDK. **Nó không phải là một API key và không thể được gửi như một API key**, đó là lý do tại sao các endpoint này chỉ hữu ích bên trong ứng dụng chính chủ.

| Endpoint | Chức năng | Body |
|---|---|---|
| `POST /team/tokens/team-member` | Cho phép thành viên nhóm bắt đầu làm việc bên trong tài khoản mà họ thuộc về. | `account_owner_uid` (bắt buộc) |
| `POST /team/tokens/return-from-team` | Đưa họ quay trở lại tài khoản của chính họ. | — |
| `POST /team/tokens/assist` | Cho phép nhân viên <span data-t="appName">Your AI Connector</span> mở tài khoản của khách hàng để hỗ trợ. Chỉ dành cho nhân viên. | `customerUid` |
| `POST /team/tokens/return-to-admin` | Kết thúc phiên hỗ trợ và đưa nhân viên quay trở lại tài khoản của chính họ. | — |
| `POST /team/tokens/agency-assist` | Cho phép một đại lý mở một trong các tài khoản phụ của khách hàng — hoặc, nếu gọi mà không có tài khoản phụ, quay trở lại tài khoản đại lý. | `subAccountUid` (tùy chọn) |

Mỗi yêu cầu sẽ bị từ chối với `403` khi phiên làm việc không có quyền thực hiện: không phải là thành viên của tài khoản đó, không phải là nhân viên, tài khoản phụ đó không thuộc đại lý của bạn hoặc chưa được cấp quyền cho bạn, hoặc phiên làm việc hiện không ở chế độ mà điểm cuối yêu cầu.

---

## Gán vai trò nền tảng

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

Thiết lập vai trò **nền tảng** của người dùng — `User`, `Dev`, `Support` hoặc `Agency`. Đây không phải là tư cách thành viên nhóm: đây là loại tài khoản <span data-t="appName">Your AI Connector</span> mà một người sở hữu.

Điểm cuối này bị giới hạn cho nhân viên <span data-t="appName">Your AI Connector</span> và `Dev` cuối cùng còn lại không thể bị hạ cấp. Được liệt kê để đảm bảo tính đầy đủ; nó không phải là một phần trong việc quản lý nhóm của riêng bạn.

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

| Trạng thái | Khi nào |
|---|---|
| `400` | `role` bị thiếu hoặc không phải là một trong bốn loại, hoặc hành động này sẽ xóa bỏ `Dev` cuối cùng. |
| `403` | Bạn không phải là nhân viên, hoặc phiên làm việc đang hoạt động trong một tài khoản khác. |
| `404` | Không tìm thấy người dùng đó. |

---

## Lỗi API nhóm

Các điểm cuối nhóm trả về phong bì lỗi tiêu chuẩn, luôn kèm theo `error_code` cùng với trạng thái HTTP:

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

| Trạng thái | Khi nào xảy ra trên điểm cuối nhóm |
|---|---|
| `400` | Một trường bắt buộc bị thiếu hoặc không hợp lệ, hoặc hành động không được phép trong trạng thái này (kích hoạt lại thành viên đã bị xóa, đình chỉ chủ sở hữu, xóa một phòng ban mà ai đó bị giới hạn quyền truy cập). |
| `401` | Bạn đã gửi khóa API đến một điểm cuối yêu cầu người dùng đã đăng nhập — xem [Xác thực](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | Bạn không có quyền `team_management`, thay đổi vượt quá quyền truy cập của bạn, hoặc hành động bị từ chối khi đang làm việc trong một tài khoản khác. |
| `404` | Không tìm thấy thành viên, lời mời, phòng ban hoặc người dùng đó. |
| `409` | Đã là thành viên nhóm, đã có lời mời đang chờ xử lý, hoặc đã tồn tại một phòng ban với tên đó. |
| `429` | Đã hết chỗ trong nhóm, đạt giới hạn 20 lời mời mỗi ngày, hoặc bạn đã đạt giới hạn tốc độ API. |
| `504` | Lời mời bạn cố gắng chấp nhận đã hết hạn. |

Các mã chia sẻ mà mọi điểm cuối có thể trả về — `429` (giới hạn tốc độ) và `500` — được liệt kê cùng với hướng dẫn thử lại trong [Lỗi & Phân trang](errors-and-pagination.md).

---

## Liên quan

- [Quản lý nhóm](../settings/team-management.md) — các tính năng tương tự trong bảng điều khiển, kèm ảnh chụp màn hình.
- [Xác thực](authentication.md) — cách gửi mã thông báo ID Firebase thay vì khóa API.
- [API Danh bạ](contacts.md) — các danh bạ mà giới hạn hiển thị của thành viên áp dụng cho.

