
# واجهة برمجة تطبيقات الفريق

فريقك هو كل من يعمل داخل حسابك بخلافك — المسؤولون، والوكلاء، والمشاهدون للقراءة فقط — بالإضافة إلى الدعوات التي أرسلتها والأقسام التي تنظمهم فيها. واجهة برمجة تطبيقات الفريق (Team API) هي النسخة البرمجية من **الإعدادات ← الفريق**: إضافة الأشخاص وإزالتهم، وتحديد ما يمكن لكل منهم رؤيته والقيام به، وإرسال الدعوات ومتابعتها، وإدارة الأقسام.

جميع نقاط النهاية أدناه نسبية إلى عنوان URL الأساسي `https://api.youraiconnector.com/v1`. للحصول على إصدار لوحة التحكم لكل ما هو موجود في هذه الصفحة، راجع [إدارة الفريق](../settings/team-management.md).

---

## المصادقة: تتطلب نقاط النهاية هذه شخصاً مسجلاً للدخول

**هذا هو الجزء الوحيد من واجهة برمجة التطبيقات الذي لا يمكن لمفتاح API استخدامه.** يجب استدعاء كل نقطة نهاية `/team` باستثناء نقاط نهاية [القسم](#departments) باستخدام **رمز تعريف Firebase (Firebase ID token)** من جلسة مسجلة الدخول:

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

أرسل مفتاح API بدلاً من ذلك وسيتم رفض الطلب بـ `401`:

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

السبب هو أن نقاط النهاية هذه تقرر ما يجب فعله بناءً على **من هو مسجل الدخول**: دورك، والحد الأقصى لما يُسمح لك بمنحه لشخص آخر، وما إذا كنت تعمل حالياً داخل حساب آخر. مفتاح API هو تكامل وليس شخصاً، لذا لا يوجد أحد لتطبيق تلك القواعد عليه.

من الناحية العملية، هذا يعني أن واجهة برمجة تطبيقات الفريق مخصصة لتطبيق الطرف الأول مع مستخدم <span data-t="appName">Your AI Connector</span> مسجل الدخول (راجع [المصادقة ← رمز تعريف Firebase](authentication.md#4-firebase-id-token-first-party-only)). لا يمكن لتكامل من خادم إلى خادم إدارة أعضاء الفريق — فلا توجد طريقة لإنشاء أحد هذه الرموز من خارج التطبيق.

> **الاستثناء:** نقاط النهاية الأربع الخاصة بـ [القسم](#departments) هي نقاط نهاية عادية لواجهة برمجة التطبيقات. وهي تقبل مفتاح API الخاص بك تماماً مثل بقية واجهة برمجة التطبيقات، بالإضافة إلى جلسة مسجلة الدخول.

تتبع كل استجابة في هذه الصفحة الغلاف المعتاد: `success: true` بالإضافة إلى حقول نقطة النهاية في المستوى الأعلى، أو `success: false` مع `error` و `error_code` عند حدوث خطأ ما.

---

## الأدوار والأذونات

لكل عضو في الفريق **دور** واحد، والذي يحدد وصوله الافتراضي عبر 12 منطقة في التطبيق. يمكنك بعد ذلك تجاوز الإعدادات للمناطق الفردية.

| الدور | القيمة | ملخص |
|---|---|---|
| المسؤول | `admin` | كل شيء باستثناء إجراءات مستوى الفوترة الخاصة بالمالك. |
| المحرر | `editor` | يمكنه إنشاء الأشياء وتغييرها. يظهر كـ **وكيل** في التطبيق. |
| المشاهد | `viewer` | للقراءة فقط. |

يتم تعيين كل منطقة إلى واحد من أربعة مستويات: `none` (مخفي)، `view` (للقراءة فقط)، `edit` (إنشاء وتغيير)، `full` (بما في ذلك الحذف).

| المنطقة | المسؤول | المحرر | المشاهد |
|---|---|---|---|
| `campaigns` | كامل | تعديل | عرض |
| `contacts` | كامل | تعديل | عرض |
| `messages` | كامل | تعديل | عرض |
| `appointments` | كامل | تعديل | عرض |
| `settings` | تعديل | عرض | لا شيء |
| `billing` | تعديل | لا شيء | لا شيء |
| `team_management` | تعديل | لا شيء | لا شيء |
| `analytics` | كامل | عرض | عرض |
| `phone_numbers` | تعديل | لا شيء | لا شيء |
| `integrations` | تعديل | لا شيء | لا شيء |
| `faqs` | كامل | تعديل | عرض |
| `daily_summaries` | كامل | عرض | عرض |

للخروج عن الإعدادات الافتراضية للدور، أرسل `permission_overrides` — وهو مصفوفة من كائنات `{ "area": ..., "level": ... }`. يستبدل كل إدخال الإعداد الافتراضي للدور في ذلك المجال المحدد؛ أما كل ما لا تدرجه فيحتفظ بالإعداد الافتراضي للدور.

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

**من يمكنه استدعاء نقاط النهاية هذه**

- يمكن **لمالك الحساب** دائماً القيام بكل شيء.
- يحتاج عضو الفريق إلى `team_management` في `view` لقراءة قائمة الأعضاء وقائمة الدعوات، وفي `edit` للإضافة أو التغيير أو التعليق أو الإزالة أو الدعوة أو الإلغاء أو إعادة الإرسال. يتمتع المسؤولون بـ `edit` افتراضياً؛ بينما يتمتع المحررون والمشاهدون بـ `none`، لذا افتراضياً لا يمكن لغير المسؤولين إدارة الفريق.
- **لا يمكن لأحد منح صلاحيات أعلى من صلاحياته.** إذا حاولت منح شخص ما مستوى لا تملكه أنت نفسك — أو تعديل أو تعليق أو إزالة شخص صلاحياته أوسع من صلاحياتك — فسيتم رفض الطلب مع `403` ورسالة تحدد المجال.

---

## كائن عضو الفريق

تُرجع `GET /team/members` واحداً من هذه لكل عضو:

| الحقل | النوع | الوصف |
|---|---|---|
| `member_uid` | string | معرف المستخدم الخاص بالعضو. هذا هو `{memberUid}` في المسارات أدناه. |
| `account_owner_uid` | string | الحساب الذي ينتمي إليه العضو. |
| `member_email` | string | عنوان بريده الإلكتروني. |
| `member_display_name` | string | الاسم الذي يظهر له في التطبيق. |
| `role` | string | `admin` أو `editor` أو `viewer`. |
| `permission_overrides` | array | استثناءاته لكل مجال. `[]` عندما يعتمد كلياً على إعدادات الدور الافتراضية. |
| `status` | string | `active` أو `suspended`. |
| `auto_assign_enabled` | boolean \| null | ما إذا كان يمكن تعيين جهات اتصال جديدة له تلقائياً. تعني `null` أنه لم يتم تغييره أبداً، وهو ما يعمل كـ `true`. |
| `created_by` | string | من قام بإضافته. |
| `created_at` | string \| null | طابع زمني بتنسيق ISO 8601. |
| `updated_at` | string \| null | طابع زمني بتنسيق ISO 8601. |

لا يتم إرجاع الأعضاء الذين تمت إزالتهم — القائمة تشمل الأعضاء النشطين والمعلقين فقط.

> **حدود الرؤية هي للكتابة فقط هنا.** يمكن تعيين `contact_scope` و `contact_scope_axes` و `sub_account_access` (انظر [تقييد ما يمكن للعضو رؤيته](#limiting-what-a-member-can-see)) عند الإنشاء والتحديث والدعوة، ولكن نقطة النهاية هذه لا تُرجعها.

---

## سرد أعضاء الفريق

`GET /team/members`

تُرجع قائمة الأعضاء بالإضافة إلى أعداد المقاعد في خطتك، حتى تتمكن من عرض "3 من 5 مقاعد" ومعرفة متى سيتم رفض الدعوات.

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

**الاستجابة**

```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` هي `null` عندما لا تحتوي خطتك على حد أقصى للمقاعد. تحسب `seats_used` الأعضاء **النشطين** فقط — تعليق أو إزالة شخص ما يحرر مقعده على الفور.

---

## إضافة عضو فريق مباشرة

`POST /team/members`

تضيف شخصاً ما إلى فريقك مباشرة، دون الحاجة إلى دعوة.

> **هذا لا يرسل أي بريد إلكتروني.** لا يتم إخطار الأشخاص بأنه تمت إضافتهم، وإذا لم يكن لديهم بالفعل تسجيل دخول <span data-t="appName">Your AI Connector</span>، فإن الحساب الذي تم إنشاؤه لهم **ليس له كلمة مرور**، لذا لا يمكنهم تسجيل الدخول حتى يقوموا بإعادة تعيينها. استخدم [إرسال دعوة](#send-an-invitation) ما لم تكن لديك طريقتك الخاصة لإخبار الشخص ومساعدته على تسجيل الدخول.

**حقول الطلب**

| الحقل | مطلوب | الوصف |
|---|---|---|
| `email` | نعم | عنوان البريد الإلكتروني الخاص بعضو الفريق. |
| `display_name` | نعم | الاسم الذي يظهر لهم في التطبيق. |
| `role` | نعم | `admin` أو `editor` أو `viewer`. |
| `permission_overrides` | لا | استثناءات لكل منطقة عن الإعدادات الافتراضية للدور. |
| `contact_scope` | لا | `all` أو `assigned` — راجع [تقييد ما يمكن للعضو رؤيته](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | لا | مع `assigned`، اسمح لهم أيضاً برؤية جهات الاتصال التي لا يملكها أحد بعد. |
| `contact_scope_axes` | لا | قصرهم على وكلاء أو قنوات أو أقسام محددة. |
| `sub_account_access` | لا | للوكالات فقط — الحسابات الفرعية للعملاء التي يمكنهم فتحها. |

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

**الاستجابة** — `201 Created`

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

| الحالة | متى |
|---|---|
| `400` | `email` أو `display_name` أو `role` مفقود، أو أن الدور ليس واحداً من الثلاثة، أو حاولت إضافة نفسك. |
| `403` | ليس لديك إذن لإدارة الفريق، أو حاولت منح صلاحيات تتجاوز صلاحياتك. |
| `409` | هذا الشخص عضو نشط بالفعل في فريقك. |
| `429` | مقاعد الفريق في خطتك ممتلئة. |

إضافة شخص كان **معلقاً أو محذوفاً** سابقاً يؤدي إلى إعادته بدلاً من الفشل.

---

## تحديث عضو في الفريق

`PATCH /team/members/{memberUid}`

يغير دور العضو، أو أذوناته، أو مستوى رؤيته، أو وصوله للعملاء، أو ما إذا كان يشارك في التعيين التلقائي لجهات الاتصال. أرسل فقط الحقول التي تريد تغييرها؛ أي شيء تتركه سيحتفظ بقيمته الحالية.

**حقول الطلب**

| الحقل | الوصف |
|---|---|
| `role` | `admin` أو `editor` أو `viewer`. |
| `permission_overrides` | يستبدل قائمة التجاوزات الخاصة بهم بالكامل. أرسل `[]` لإعادتهم إلى الإعدادات الافتراضية للدور فقط. |
| `status` | يتم قبول `active` فقط، لإعادة عضو معلق. لتعليق شخص ما، استخدم [نقطة نهاية التعليق](#suspend-a-team-member). |
| `auto_assign_enabled` | `true` أو `false`. |
| `contact_scope` | `all` أو `assigned`. |
| `contact_scope_unassigned` | `true` أو `false`. |
| `contact_scope_axes` | راجع [تقييد ما يمكن للعضو رؤيته](#limiting-what-a-member-can-see). |
| `sub_account_access` | للوكالات فقط. |

> **هذه هي نقطة النهاية الوحيدة التي تعني فيها `null` "مسح".** إرسال `"contact_scope": null` أو `"contact_scope_axes": null` أو `"sub_account_access": null` يزيل ذلك القيد تماماً ويعيد العضو لرؤية كل شيء. عند الإنشاء والدعوة، تعني `null` ببساطة "غير مزود".

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

**الاستجابة**

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

| الحالة | متى |
|---|---|
| `400` | قيمة `status` أو `auto_assign_enabled` غير صالحة، أو حاولت إعادة تنشيط عضو تم حذفه (يجب إعادة دعوة الأعضاء المحذوفين). |
| `403` | ليس لديك إذن، أو أن التغيير سيؤدي إلى تعديل أو إنشاء وصول أوسع من وصولك. |
| `404` | لا يوجد عضو فريق بهذا الاسم. |

---

## تعليق عضو في الفريق

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

يعلق شخصاً ما: يحتفظ بمكانه في الفريق لكنه يفقد صلاحية الوصول. استخدم هذا بدلاً من الحذف عندما يكون التوقف مؤقتاً — أعدهم باستخدام `PATCH /team/members/{memberUid}` و `{"status": "active"}`.

**cURL**

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

**الاستجابة**

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

العضو المعلق **يُخلي مقعده**، لذا يمكنك دعوة شخص آخر مكانه. تنتهي صلاحية وصولهم عند تحديث رمز الجلسة الحالي الخاص بهم، وهو ما قد يستغرق ما يصل إلى ساعة — احذفهم بدلاً من ذلك إذا كنت بحاجة إلى أن يكون الأمر فورياً.

| الحالة | متى |
|---|---|
| `400` | حاولت تعليق مالك الحساب، أو عضواً معلقاً أو محذوفاً بالفعل. |
| `403` | صلاحية وصولهم أوسع من صلاحيتك. |
| `404` | لا يوجد عضو فريق بهذا الاسم. |

---

## إزالة عضو من الفريق

`DELETE /team/members/{memberUid}`

تؤدي هذه العملية إلى إزالة شخص ما من فريقك وتوفير مقعده. سيتم تسجيل خروج العضو وفقدان وصوله إلى حسابك؛ بينما يظل تسجيل دخوله الخاص دون تغيير.

**cURL**

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

**الاستجابة**

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

تعد الإزالة إجراءً دائماً من جانبك: **لا يمكن إعادة تفعيل** العضو الذي تمت إزالته باستخدام نقطة نهاية التحديث (update endpoint) — قم بدعوته مجدداً إذا غيرت رأيك. كما تتم إزالة بريده الإلكتروني من قائمة إشعارات حسابك.

| الحالة | متى |
|---|---|
| `400` | حاولت إزالة مالك الحساب. |
| `403` | صلاحيات وصوله أوسع من صلاحياتك. |
| `404` | عضو الفريق غير موجود. |

---

## تقييد ما يمكن للعضو رؤيته

هناك ثلاثة حقول اختيارية، مقبولة عند [الإضافة](#add-a-team-member-directly) و[التحديث](#update-a-team-member) و[الدعوة](#send-an-invitation)، تحدد مقدار ما يراه الشخص في الحساب. وهي تراكمية: العضو المقيد بأكثر من خيار يخضع لجميع هذه القيود.

**`contact_scope`** — `all` (الخيار الافتراضي: كل جهات الاتصال والمحادثات) أو `assigned` (فقط تلك المسندة إليهم). مع `assigned`، أضف `"contact_scope_unassigned": true` للسماح لهم أيضاً برؤية جهات الاتصال التي لم يتم تعيين مالك لها بعد.

**`contact_scope_axes`** — تقيدهم بوكلاء أو قنوات أو أقسام محددة:

| الحقل | النوع | الوصف |
|---|---|---|
| `agents` | string[] | معرفات الوكلاء. لا يرون سوى المحادثات الموجهة إلى أحد هؤلاء الوكلاء. الحد الأقصى 200. |
| `channels` | string[] | أسماء القنوات — `whatsapp`، `whatsapp_web`، `sms`، `instagram`، `instagram_private`، `messenger`، `facebook`، `chat_widget`، `telegram`، `line`، `viber`، `tiktok`، `imessage`، `email`، `linkedin`، `skool`، `custom`، `custom_channel`. الحد الأقصى 200. |
| `departments` | string[] | معرفات الأقسام (انظر [الأقسام](#departments)). لا يرون سوى العملاء المحتملين المسجلين تحتها. الحد الأقصى 200. |
| `include_unrouted` | boolean | عند ضبط `agents`، يتم أيضاً عرض المحادثات التي لا يتولاها أي وكيل. معطل افتراضياً. يتم تجاهله عندما يكون `agents` فارغاً. |
| `include_undepartmented` | boolean | عند ضبط `departments`، يتم أيضاً عرض المحادثات التي لا تتبع أي قسم. معطل افتراضياً. يتم تجاهله عندما يكون `departments` فارغاً. |

لا يتم التحقق من معرفات الوكلاء والأقسام عند حفظها — المعرف الذي لا وجود له لا يطابق شيئاً ببساطة، مما يظهر كصندوق وارد فارغ بدلاً من ظهور خطأ. أما أسماء القنوات **فيتم** التحقق منها: أي اسم غير معروف يتم رفضه مع `400`.


لا يمكن ضبط أي من هذه الخيارات الثلاثة لمالك الحساب — سيتم رفض هذا الطلب مع `400`.

---

## سرد الدعوات

`GET /team/invites`

الدعوات التي أرسلتها، مرتبة من الأحدث إلى الأقدم، حتى تتمكن من معرفة من لم يقبل الدعوة بعد.

**معلمات الاستعلام**

| المعلمة | مطلوبة | الوصف |
|---|---|---|
| `status` | لا | إرجاع الدعوات الموجودة في هذه الحالة فقط — `pending` أو `accepted` أو `declined` أو `cancelled` أو `expired`. |

**cURL**

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

**الاستجابة**

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

لا يتم إرجاع رمز الدعوة مطلقاً — فهو موجود فقط في البريد الإلكتروني الذي تم إرساله.

---

## إرسال دعوة

`POST /team/invites`

إرسال دعوة عبر البريد الإلكتروني لشخص ما للانضمام إلى فريقك. هذه هي الطريقة المعتادة لإضافة زميل في الفريق: حيث ينقرون على الرابط، ويسجلون الدخول بحساباتهم الخاصة، ويقبلون الدعوة. إذا لم يكن لديهم حساب <span data-t="appName">Your AI Connector</span> بعد، فسيتم إنشاء حساب لهم، وسيرشدهم البريد الإلكتروني خلال عملية تعيين كلمة مرور.

**حقول الطلب**

| الحقل | مطلوب | الوصف |
|---|---|---|
| `email` | نعم | عنوان البريد الإلكتروني المراد إرسال الدعوة إليه. |
| `role` | نعم | `admin` أو `editor` أو `viewer`. |
| `permission_overrides` | لا | استثناءات لكل منطقة، يتم تطبيقها بمجرد قبولهم للدعوة. |
| `contact_scope` | لا | يتم تطبيقها عند قبولهم للدعوة. |
| `contact_scope_unassigned` | لا | يتم تطبيقها عند قبولهم للدعوة. |
| `contact_scope_axes` | لا | يتم تطبيقها عند قبولهم للدعوة. |
| `sub_account_access` | لا | للوكالات فقط. يتم تطبيقها عند قبولهم للدعوة. |

يعني إعداد الأذونات مسبقاً أنك لست بحاجة إلى تعديل العضو لاحقاً — حيث يتم نسخ كل شيء إلى عضويتهم عند قبولهم للدعوة.

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

**الاستجابة** — `201 Created`

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

**أمور يجب التخطيط لها**

- **تنتهي صلاحية الدعوات بعد 7 أيام.** يمكن إعادة إرسال الدعوة منتهية الصلاحية، مما يبدأ فترة 7 أيام جديدة.
- **الدعوات المعلقة تحجز مقعداً.** على عكس إضافة عضو مباشرة، فإن التحقق من المقاعد هنا يحسب الأعضاء النشطين *بالإضافة إلى* الدعوات المعلقة، لذا سيتم رفض الطلب إذا كان الحساب قد استنفد جميع مقاعده قبل إرسال البريد الإلكتروني.
- **20 دعوة في اليوم**، يتم احتسابها لكل حساب عبر الإرسال وإعادة الإرسال.

| الحالة | متى |
|---|---|
| `400` | `email` مفقود أو الدور غير صالح. |
| `403` | ليس لديك إذن لإدارة الفريق، أو حاولت منح وصول يتجاوز صلاحياتك. |
| `409` | توجد بالفعل دعوة معلقة لهذا البريد الإلكتروني، أو أن هذا الشخص موجود بالفعل في فريقك. |
| `429` | مقاعد الفريق في خطتك ممتلئة، أو أنك وصلت إلى حد 20 دعوة في اليوم. توضح رسالة `error` السبب. |

---

## إلغاء دعوة

`DELETE /team/invites/{inviteId}`

يؤدي هذا إلى سحب الدعوة قبل قبولها. سيتوقف الرابط الموجود في البريد الإلكتروني عن العمل.

**cURL**

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

**الاستجابة**

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

يمكن إلغاء كل من دعوات `pending` و `expired`. الدعوة التي تم قبولها أو رفضها أو إلغاؤها بالفعل تُرجع `400`؛ والدعوة التي لا تخصك تُرجع `403`؛ والمعرف غير المعروف يُرجع `404`.

---

## إعادة إرسال دعوة

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

يعيد إرسال بريد الدعوة الإلكتروني — في حال فات المستخدم أو وصل إلى البريد المزعج (Spam). يعمل مع دعوات `pending` و `expired`، ويعيد ضبط تاريخ انتهاء الصلاحية ليكون بعد 7 أيام من الآن.

**cURL**

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

**الاستجابة**

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

يحتوي البريد الإلكتروني الجديد على رابط جديد، **ويستمر الرابط القديم في العمل أيضاً**، لذا لن يواجه الشخص الذي يعثر على البريد الإلكتروني الأول لاحقاً أي مشكلة. تُحتسب إعادة الإرسال ضمن نفس حد الـ 20 دعوة يومياً المطبق على الإرسال، وتؤدي إعادة تفعيل دعوة *منتهية الصلاحية* إلى إعادة التحقق من المقاعد المتاحة لديك — سيتم رفض الطلب في حال كانت الخطة ممتلئة مع ظهور `429`.

---

## قبول دعوة

`POST /team/invites/accept`

يقبل دعوة باستخدام الرمز المميز (token) الموجود في بريد الدعوة الإلكتروني، مما يضيف الشخص الذي سجل دخوله إلى فريق ذلك الحساب.

> **هذا إجراء يتعلق بهويتك الشخصية.** سجل الدخول بصفتك الشخصية — يتم رفض هذا الإجراء عمداً مع ظهور `403` أثناء عملك داخل حساب شخص آخر.

**حقول الطلب**

| الحقل | مطلوب | الوصف |
|---|---|---|
| `invite_token` | نعم | الرمز المميز (token) من رابط بريد الدعوة الإلكتروني. |

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

**الاستجابة**

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

| الحالة | متى |
|---|---|
| `400` | `invite_token` مفقود، أو أن الدعوة موجهة لحسابك الخاص. |
| `403` | الجلسة تعمل داخل حساب آخر، أو أن الدعوة أُرسلت إلى عنوان بريد إلكتروني مختلف عن العنوان الذي سجلت دخولك به. |
| `404` | الدعوة غير موجودة أو تم استخدامها بالفعل. |
| `429` | امتلأت مقاعد الحساب بين وقت إرسال الدعوة ووقت قبولك لها. |
| `504` | انتهت صلاحية الدعوة. اطلب من المرسل إعادة إرسالها. |

---

## رفض دعوة

`POST /team/invites/decline`

يرفض دعوة باستخدام الرمز المميز (token) الموجود في البريد الإلكتروني. مثل القبول، هذا إجراء يتعلق بهويتك الشخصية ويتم رفضه أثناء عملك داخل حساب آخر.

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

**الاستجابة**

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

---

## الأقسام

**القسم** هو مجموعة مسماة من فريقك — المبيعات، دعم العملاء، الموارد البشرية. وهو يمنح العميل المحتمل فريقاً مسؤولاً، ويمكنه المطالبة بمحادثات جديدة بمفرده، كما يمكن استخدامه لتقييد ما يراه العضو.

> **تتطلب نقاط النهاية الأربع هذه مفتاح API.** على عكس بقية هذه الصفحة، فهي تتمتع بالمصادقة مثل أي نقطة نهاية أخرى في واجهة برمجة التطبيقات (راجع [المصادقة](authentication.md)). تعمل جلسة تسجيل الدخول أيضاً: تتطلب القراءة `contacts` في `view`، ويتطلب الإنشاء أو التغيير أو الحذف `team_management` في `edit`.

**كائن القسم**

| الحقل | النوع | الوصف |
|---|---|---|
| `id` | string | معرف القسم. استخدمه في `contact_scope_axes.departments` وفي المسارات أدناه. |
| `name` | string | اسم الفريق. بحد أقصى 60 حرفاً، ويجب أن يكون فريداً في الحساب. |
| `color` | string \| null | لون التمييز كـ `#rrggbb`، أو `null`. |
| `member_uids` | string[] | أعضاء الفريق في هذا القسم. قد يشمل مالك الحساب. |
| `auto_assign_enabled` | boolean | ما إذا كان العميل المحتمل المسجل تحت هذا القسم يتم تسليمه أيضاً لشخص ما فيه. تعني `false` أن القسم يعمل من قائمة انتظار مشتركة. |
| `routing_agents` | string[] | المحادثات الجديدة التي تتعامل معها وكلاء الذكاء الاصطناعي هؤلاء يتم تسجيلها تحت هذا القسم تلقائياً. تعني القيمة الفارغة عدم وجود قاعدة وكيل. |
| `routing_channels` | string[] | المحادثات الجديدة على هذه القنوات يتم تسجيلها هنا تلقائياً. تعني القيمة الفارغة عدم وجود قاعدة قناة. |
| `created_by` | string \| null | من قام بإنشائه. |

عند ضبط كل من `routing_agents` و `routing_channels`، يجب أن تطابق المحادثة **كلاهما** ليتم تسجيلها هنا — هكذا تمنح فريقاً واحداً "وكيل الدعم، ولكن على واتساب فقط".

يمكن أن يحتوي الحساب على ما يصل إلى **50** قسماً.

### سرد الأقسام

`GET /team/departments`

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

**الاستجابة**

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

### إنشاء قسم

`POST /team/departments`

**حقول الطلب**

| الحقل | مطلوب | الوصف |
|---|---|---|
| `name` | نعم | بحد أقصى 60 حرفاً. يجب ألا يطابق قسماً موجوداً. |
| `color` | لا | `#rrggbb` ست عشري، أو `null`. |
| `member_uids` | لا | من هو المسؤول عنه. يجب أن يكون كل UID هو مالك الحساب أو عضواً **نشطاً** في الفريق. |
| `auto_assign_enabled` | لا | القيمة الافتراضية هي `true`. |
| `routing_agents` | لا | معرفات الوكلاء الذين يتم توجيه محادثاتهم الجديدة إلى هنا. |
| `routing_channels` | لا | أسماء القنوات التي يتم توجيه محادثاتهم الجديدة إلى هنا — نفس مفردات `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"]
```

**الاستجابة** — `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"
  }
}
```

| الحالة | متى |
|---|---|
| `400` | `name` مفقود أو طويل جداً، `color` ليس `#rrggbb`، اسم القناة غير معروف، معرف المستخدم المدرج ليس عضواً نشطاً في هذا الفريق، أو لديك بالفعل 50 قسماً. |
| `409` | يوجد قسم بهذا الاسم بالفعل. |

### تحديث قسم

`PATCH /team/departments/{departmentId}`

تغيير قسم. يتم تغيير الحقول التي ترسلها فقط.

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

**الاستجابة**

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

إرسال حقول غير معروفة يُرجع `400`؛ وإرسال قسم غير معروف يُرجع `404`؛ واسم يتعارض مع قسم آخر يُرجع `409`.

### حذف قسم

`DELETE /team/departments/{departmentId}`

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

**الاستجابة**

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

> **يتم رفض حذف قسم يقتصر عليه وصول شخص ما.** يُرجع الرد `400` أسماء الأعضاء الذين تقتصر رؤيتهم على هذا القسم، حتى تتمكن من إعادة تحديد نطاق وصولهم أولاً. هذا الإجراء متعمد: فإلغاء تقييد وصولهم بصمت سيمنحهم حق الوصول إلى قاعدة عملائك بالكامل دون أي إشارة إلى حدوث ذلك.

جهات الاتصال المسجلة تحت قسم محذوف لا يتم تعديلها — بل تتوقف ببساطة عن إظهار أي قسم، وفي المرة التالية التي تقوم فيها بتسجيلها، سيتم حفظها بشكل طبيعي.

---

## تحقق من أذوناتك الخاصة

`GET /team/permissions`

يُرجع ما يُسمح للشخص المسجل دخوله بالقيام به في الحساب الذي يعمل فيه حالياً. استخدمه لإخفاء الأزرار التي لا يمكن للعضو استخدامها، بدلاً من السماح له باكتشاف هذا القيد من خلال رسالة خطأ.

**cURL**

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

**الرد — مالك الحساب**

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

**الرد — عضو فريق يعمل داخل حساب**

```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` هو `owner` عندما يكون الشخص المسجل دخوله هو مالك الحساب؛ وإلا فإنه يمثل دور الفريق الخاص به. يظهر `member` فقط في وضع الفريق، ويحمل `contact_scope` و `contact_scope_unassigned` و `contact_scope_axes` عندما تتضمن عضويتهم ذلك.

---

## رموز الجلسة

تقوم خمس نقاط نهاية بإنشاء رمز تسجيل دخول لمرة واحدة للتبديل بين الحسابات. جميعها تستجيب بنفس الطريقة:

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

يتم استبدال الرمز بجلسة باستخدام حزمة تطوير البرامج (SDK) الخاصة بـ Firebase. **هذا الرمز ليس مفتاح API ولا يمكن إرساله كمفتاح**، ولهذا السبب لا تكون نقاط النهاية هذه مفيدة إلا داخل تطبيق تابع للطرف الأول.

| نقطة النهاية | وظيفتها | النص الأساسي |
|---|---|---|
| `POST /team/tokens/team-member` | تتيح لعضو الفريق البدء في العمل داخل حساب ينتمي إليه. | `account_owner_uid` (مطلوب) |
| `POST /team/tokens/return-from-team` | تعيدهم للخارج، إلى حسابهم الخاص. | — |
| `POST /team/tokens/assist` | تتيح لموظفي <span data-t="appName">Your AI Connector</span> فتح حساب عميل للمساعدة. للموظفين فقط. | `customerUid` |
| `POST /team/tokens/return-to-admin` | تنهي جلسة المساعدة وتعيد الموظف إلى حسابه الخاص. | — |
| `POST /team/tokens/agency-assist` | تتيح للوكالة فتح أحد حساباتها الفرعية للعملاء — أو، عند استدعائها بدون حساب، العودة إلى حساب الوكالة. | `subAccountUid` (اختياري) |

يرفض كل منها بـ `403` عندما لا تكون الجلسة مخولة بذلك: كأن لا يكون المستخدم عضواً في ذلك الحساب، أو ليس من الموظفين، أو أن الحساب الفرعي ليس ضمن وكالتك أو لم يتم منحه لك، أو أن الجلسة ليست حالياً في الوضع الذي تنهيه نقطة النهاية.

---

## تعيين دور المنصة

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

يحدد دور **المنصة** الخاص بالمستخدم — `User`، أو `Dev`، أو `Support`، أو `Agency`. هذا ليس عضوية في الفريق: بل هو نوع حساب <span data-t="appName">Your AI Connector</span> الذي يمتلكه الشخص.

تقتصر نقطة النهاية هذه على موظفي <span data-t="appName">Your AI Connector</span>، ولا يمكن خفض رتبة آخر `Dev` متبقٍ. مدرجة للاكتمال؛ وهي ليست جزءاً من إدارة فريقك الخاص.

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

| الحالة | متى |
|---|---|
| `400` | `role` مفقود أو ليس واحداً من الأربعة، أو أن هذا الإجراء سيؤدي إلى إزالة آخر `Dev`. |
| `403` | أنت لست من الموظفين، أو أن الجلسة تعمل داخل حساب آخر. |
| `404` | لا يوجد مثل هذا المستخدم. |

---

## أخطاء واجهة برمجة تطبيقات الفريق

تُرجع نقاط نهاية الفريق غلاف الخطأ القياسي، دائماً مع `error_code` بجانب حالة HTTP:

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

| الحالة | متى يحدث ذلك في نقطة نهاية الفريق |
|---|---|
| `400` | حقل مطلوب مفقود أو غير صالح، أو أن الإجراء غير مسموح به في هذه الحالة (إعادة تنشيط عضو تمت إزالته، تعليق المالك، حذف قسم يقتصر عليه شخص ما). |
| `401` | أرسلت مفتاح API إلى نقطة نهاية تحتاج إلى شخص مسجل الدخول — راجع [المصادقة](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | ليس لديك إذن `team_management`، أو أن التغيير يتجاوز صلاحية وصولك، أو تم رفض الإجراء أثناء العمل داخل حساب آخر. |
| `404` | لا يوجد مثل هذا العضو أو الدعوة أو القسم أو المستخدم. |
| `409` | العضو موجود بالفعل في الفريق، أو توجد دعوة معلقة بالفعل، أو يوجد قسم بهذا الاسم. |
| `429` | مقاعد الفريق ممتلئة، أو تم الوصول إلى حد 20 دعوة في اليوم، أو وصلت إلى حد معدل استخدام API. |
| `504` | انتهت صلاحية الدعوة التي حاولت قبولها. |

الرموز المشتركة التي يمكن لكل نقطة نهاية إرجاعها — `429` (حد المعدل) و `500` — مدرجة مع إرشادات إعادة المحاولة في [الأخطاء والترقيم](errors-and-pagination.md).

---

## ذات صلة

- [إدارة الفريق](../settings/team-management.md) — نفس الميزات في لوحة التحكم، مع لقطات شاشة.
- [المصادقة](authentication.md) — كيفية إرسال رمز معرف Firebase بدلاً من مفتاح API.
- [واجهة برمجة تطبيقات جهات الاتصال](contacts.md) — جهات الاتصال التي تنطبق عليها قيود رؤية العضو.

