
# المواعيد

تتيح لك واجهة برمجة تطبيقات المواعيد (Appointments API) حجز مواعيد لجهات اتصالك ضمن أنواع الأحداث الخاصة بك، ثم جلبها أو سردها أو تحديثها أو إلغاؤها أو حذفها. كما أنها تجيب على السؤال الذي يطرح أولاً في معظم عمليات الحجز — ما هي الأوقات المتاحة فعلياً — وتغطي جانب التقويم: سرد تقويمات Google التي قمت بربطها واستيراد الأحداث الموجودة فيها بالفعل. عند تفعيل اتصال تقويم Google، يتم إنشاء حدث التقويم المطابق ومزامنته تلقائياً في الخلفية. يمكن أيضاً التحقق من المطاعم التي تستخدم Zenchef أو Formitable لأنظمة الحجز الخاصة بها وربطها هنا، بحيث يقوم وكيل الذكاء الاصطناعي بحجز طاولات حقيقية بدلاً من المواعيد الداخلية.

جميع المسارات في هذه الصفحة نسبية إلى عنوان URL الأساسي `https://api.youraiconnector.com/v1`. يتطلب كل طلب مفتاح واجهة برمجة التطبيقات الخاص بك — راجع [المصادقة](authentication.md) للحصول على القائمة الكاملة لطرق إرساله. تستخدم الأمثلة أدناه ترويسة `X-API-Key`، مع مثال cURL واحد يوضح نموذج استعلام `?apiKey=` أيضًا.

> **الأحداث مقابل المواعيد:** *نوع الحدث* هو تعريف لفترة زمنية قابلة للحجز (نوع الاجتماع، مدته، غرفه). *الموعد* هو حالة حجز واحدة لنوع حدث معين لجهة اتصال محددة. يمكنك حجز موعد عن طريق الإشارة إلى جهة الاتصال ونوع الحدث.

---

## كائن الموعد

كل نقطة نهاية تُرجع موعدًا تستخدم نفس الهيكل:

| الحقل | الوصف |
|---|---|
| `id` | المعرف الفريد للموعد. |
| `contact_id` | معرف جهة الاتصال التي تم حجز الموعد معها. |
| `event_id` | معرف نوع الحدث الذي تم حجز الموعد عليه. |
| `status` | `Confirmed` أو `Canceled`. |
| `start_time` | وقت بدء الموعد، بتنسيق ISO 8601 بالتوقيت العالمي المنسق (UTC). |
| `end_time` | وقت انتهاء الموعد، بتنسيق ISO 8601 بالتوقيت العالمي المنسق (UTC). |
| `created_at` | وقت إنشاء الموعد. |
| `last_modified_at` | وقت آخر تعديل للموعد. |
| `room_name` | الغرفة أو المورد الذي تم حجز الموعد فيه، عندما يستخدم نوع الحدث غرفًا. |
| `description` | وصف حر للموعد. |
| `summary` | ملخص قصير أو عنوان. |
| `cancelation_reason` | السبب المقدم عند إلغاء الموعد، إن وجد. |
| `google_calendar_event_id` | معرف حدث تقويم Google المرتبط. يتم تعيينه بمجرد اكتمال مزامنة التقويم؛ `null` عند عدم توصيل أي تقويم أو أثناء استمرار المزامنة. |
| `calendar_synced` | `true` بمجرد ربط الموعد بحدث تقويم. |
| `imported` | `true` عندما يتم استيراد الموعد من تقويم خارجي بدلاً من حجزه مباشرة. |
| `is_recurring` | `true` عندما يكون الموعد جزءًا من سلسلة متكررة. |
| `recurrence_frequency` | مدى تكرار الموعد، عند كونه متكررًا. |
| `recurring_event_id` | معرف السلسلة المتكررة التي ينتمي إليها هذا الموعد. |
| `recurring_interval` | الفاصل الزمني بين التكرارات، عند كونه متكررًا. |
| `recurring_sequence` | موقع هذا الموعد ضمن سلسلته المتكررة. |
| `end_after_x_occurrences` | عدد مرات التكرار التي تنتهي بعدها السلسلة المتكررة. |
| `booking_provider` | نظام المصدر الذي جاء منه الحجز، عند الحجز من خلال مزود حجز متصل. |

> **حول مزامنة التقويم:** مباشرة بعد حجز موعد أو تغييره، قد يظل `google_calendar_event_id` هو `null` وقد يكون `calendar_synced` هو `false` لأن المزامنة تعمل في الخلفية بعد لحظات. قم بجلب الموعد مرة أخرى بعد فترة قصيرة لرؤية حقول التقويم المعبأة.

---

## العثور على الفترات المتاحة

`GET /appointments/available-slots`

تُرجع الأوقات المتاحة فعلياً لنوع حدث معين بين لحظتين زمنيتين. عادة ما يكون هذا هو الاستدعاء **الأول** في عملية الحجز: اعرض هذه الفترات، واسمح للشخص باختيار واحدة منها، ثم أرسل الوقت المختار إلى [حجز موعد](#book-an-appointment).

تأخذ الإجابة في الاعتبار بالفعل ساعات عمل نوع الحدث وطول الفترة الزمنية، وغرفه، والمواعيد التي حجزتها عليه مسبقاً، وكل ما هو محجوز في تقويمات Google المرتبطة — لذا فإن أي فترة تظهر هنا هي فترة يمكنك حجزها.

| معامل الاستعلام | مطلوب | الوصف |
|---|---|---|
| `event_id` | نعم | نوع الحدث المراد التحقق منه. يجب أن ينتمي إلى حسابك. |
| `start_time` | نعم | بداية النافذة الزمنية التي تريد فترات لها، بتنسيق ISO 8601 للتاريخ والوقت. |
| `end_time` | نعم | نهاية النافذة الزمنية، بتنسيق ISO 8601 للتاريخ والوقت. يتم تضمين يوم النهاية بالكامل. |

تظهر النتائج مجمعة حسب اليوم — وعندما يستخدم نوع الحدث غرفاً، تظهر مجموعة واحدة لكل غرفة في كل يوم:

| الحقل | الوصف |
|---|---|
| `date` | اليوم الذي تغطيه المجموعة، مكتوباً بصيغة `DD/MM/YYYY`. |
| `day` | اسم يوم الأسبوع بأحرف صغيرة، على سبيل المثال `monday`. |
| `room_name` | الغرفة أو المورد الذي تنتمي إليه هذه المجموعة، عندما يستخدم نوع الحدث غرفاً. |
| `available_slots` | الفترات القابلة للحجز في ذلك اليوم، مرتبة من الأقدم إلى الأحدث. |

يحتوي كل إدخال في `available_slots` على:

| الحقل | الوصف |
|---|---|
| `start_time` | بداية الفترة بصيغة `HH:mm`. |
| `end_time` | نهاية الفترة بصيغة `HH:mm`. |
| `available` | `true` — يتم إرجاع الوقت المتاح فقط. |
| `spots_left` | عدد المواعيد التي لا تزال تتسع لها هذه الفترة. تظهر فقط في أنواع الأحداث التي تقبل أكثر من حجز واحد لكل فترة. |

> **الأوقات محلية لنوع الحدث، وليست بتوقيت UTC.** قيم `date` و `start_time` و `end_time` هي قيم ساعة الحائط في المنطقة الزمنية الخاصة بنوع الحدث (تجاوزه، أو المنطقة الزمنية لحسابك في حال عدم وجود تجاوز). يتوقع [حجز موعد](#book-an-appointment) لحظة زمنية بتنسيق ISO 8601 UTC، لذا قم بتحويل الفترة التي اخترتها قبل إرسالها.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments/available-slots?event_id=event_xyz789&start_time=2026-06-15T00:00:00.000Z&end_time=2026-06-19T00:00:00.000Z" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  event_id: "event_xyz789",
  start_time: "2026-06-15T00:00:00.000Z",
  end_time: "2026-06-19T00:00:00.000Z",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments/available-slots?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.data);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/available-slots",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T00:00:00.000Z",
        "end_time": "2026-06-19T00:00:00.000Z",
    },
)
print(res.json()["data"])
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "data": [
    {
      "date": "15/06/2026",
      "day": "monday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "10:00", "end_time": "10:30", "available": true },
        { "start_time": "10:30", "end_time": "11:00", "available": true }
      ]
    },
    {
      "date": "16/06/2026",
      "day": "tuesday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "09:00", "end_time": "09:30", "available": true, "spots_left": 2 }
      ]
    }
  ]
}
```

اليوم الذي لا يحتوي على أي وقت متاح ببساطة لا يظهر. فقدان `event_id` أو `start_time` أو `end_time` يُرجع `400`؛ ونوع الحدث غير الموجود في حسابك يُرجع `404`.

---

## حجز موعد

`POST /appointments`

يحجز موعدًا جديدًا لجهة اتصال على أحد أنواع الأحداث الخاصة بك. يتم حساب وقت الانتهاء تلقائيًا من مدة الفترة الزمنية لنوع الحدث.

يتم فحص الحجز بحثًا عن تعارضات: إذا كانت الفترة المطلوبة تتداخل مع موعد مؤكد موجود مسبقًا على نفس نوع الحدث، يفشل الطلب مع `409` ولا يتم إنشاء أي شيء.

| الحقل | مطلوب | الوصف |
|---|---|---|
| `contact_id` | نعم | معرف جهة الاتصال المراد الحجز لها. يجب أن تنتمي إلى حسابك. |
| `event_id` | نعم | معرف نوع الحدث المراد الحجز عليه. يجب أن ينتمي إلى حسابك. |
| `start_time` | نعم | وقت البدء المطلوب كتاريخ ووقت بتنسيق ISO 8601. |
| `room_name` | لا | اسم الغرفة أو المورد، عندما يستخدم نوع الحدث غرفًا. |

**cURL** (باستخدام نموذج استعلام `?apiKey=`)

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "start_time": "2026-06-15T10:00:00.000Z",
    "room_name": "Room A"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contact_id: "contact_abc123",
    event_id: "event_xyz789",
    start_time: "2026-06-15T10:00:00.000Z",
    room_name: "Room A",
  }),
});
const data = await res.json();
console.log(data.appointment_id);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "contact_id": "contact_abc123",
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T10:00:00.000Z",
        "room_name": "Room A",
    },
)
print(res.json()["appointment_id"])
```

**الاستجابة** (`201 Created`):

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "created_at": "2026-06-10T09:00:00.000Z",
    "last_modified_at": "2026-06-10T09:00:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": null,
    "calendar_synced": false
  }
}
```

---

## الحصول على موعد

`GET /appointments/{appointmentId}`

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

**cURL**

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

**JavaScript**

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

**Python**

```python
import requests

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

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": "abc123googleevent",
    "calendar_synced": true
  }
}
```

---

## سرد المواعيد

`GET /appointments`

سرد المواعيد الخاصة بحسابك، بدءاً من الأحدث، مع استخدام الترقيم الصفحي المستند إلى المؤشر.

| معلمة الاستعلام | مطلوبة | الوصف |
|---|---|---|
| `contact_id` | لا | إرجاع المواعيد الخاصة بهذا جهة الاتصال فقط. تتضمن القوائم المفلترة حسب جهة الاتصال **المواعيد المؤكدة فقط** |
| `date` | لا | إرجاع المواعيد في يوم التقويم هذا فقط (`YYYY-MM-DD`). **يتطلب `contact_id`.** |
| `status` | لا | التصفية حسب `Confirmed` أو `Canceled`. متاح فقط **بدون** `contact_id`. |
| `limit` | لا | حجم الصفحة، عدد صحيح بين 1 و100. القيمة الافتراضية `50`. |
| `cursor` | لا | قيمة `next_cursor` من استجابة سابقة. |

بضع قواعد يجب وضعها في الاعتبار:

- **بدون عوامل تصفية**، ستحصل على كل موعد في الحساب، صفحة بصفحة.
- **حسب جهة الاتصال** — اضبط `contact_id` لرؤية المواعيد المؤكدة لجهة اتصال واحدة. يمكنك تضييق نطاق ذلك ليوم واحد عن طريق تمرير `date` أيضاً.
- **حسب الحالة** — اضبط `status` (بدون `contact_id`) لسرد المواعيد `Confirmed` فقط أو `Canceled` فقط عبر الحساب.
- عامل التصفية `date` بدون `contact_id`، أو `status=Canceled` مع `contact_id`، يرجع `400`.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments?contact_id=contact_abc123&date=2026-06-15" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  contact_id: "contact_abc123",
  date: "2026-06-15",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointments, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"contact_id": "contact_abc123", "date": "2026-06-15"},
)
data = res.json()
print(data["appointments"], data["next_cursor"])
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "appointments": [
    {
      "id": "aBcD1234eFgH5678",
      "contact_id": "contact_abc123",
      "event_id": "event_xyz789",
      "status": "Confirmed",
      "start_time": "2026-06-15T10:00:00.000Z",
      "end_time": "2026-06-15T10:30:00.000Z",
      "calendar_synced": true
    }
  ],
  "next_cursor": null
}
```

للتنقل بين النتائج، مرر `next_cursor` من استجابة واحدة كـ `cursor` للطلب التالي. استمر في ذلك حتى تصبح `next_cursor` مساوية لـ `null`. راجع [الأخطاء والترقيم الصفحي](errors-and-pagination.md) لمعرفة نمط الترقيم الصفحي المشترك.

---

## تحديث موعد

`PUT /appointments/{appointmentId}`

إعادة جدولة موعد أو تغيير تفاصيله. أرسل فقط الحقول التي تريد تغييرها — يلزم حقل واحد على الأقل. يجب أن يظل وقت البدء ووقت الانتهاء مجتمعين بترتيب زمني (يجب أن يكون `end_time` بعد `start_time`). تتم مزامنة التغييرات مع حدث التقويم المرتبط تلقائياً.

| الحقل | الوصف |
|---|---|
| `start_time` | تاريخ ووقت البدء الجديد بتنسيق ISO 8601. |
| `end_time` | تاريخ ووقت الانتهاء الجديد بتنسيق ISO 8601. يجب أن يكون بعد وقت البدء. |
| `room_name` | اسم الغرفة أو المورد الجديد. |
| `description` | الوصف الجديد، أو `null` لمسحه. |
| `summary` | الملخص الجديد، أو `null` لمسحه. |

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      start_time: "2026-06-16T10:00:00.000Z",
      end_time: "2026-06-16T10:30:00.000Z",
    }),
  }
);
const data = await res.json();
console.log(data.appointment);
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "start_time": "2026-06-16T10:00:00.000Z",
        "end_time": "2026-06-16T10:30:00.000Z",
    },
)
print(res.json()["appointment"])
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z",
    "calendar_synced": true
  }
}
```

---

## إلغاء موعد

`POST /appointments/{appointmentId}/cancel`

يؤدي هذا إلى إلغاء موعد مؤكد، مع إمكانية تسجيل سبب للإلغاء. يظل الموعد في حسابك بحالة `Canceled`، ويتم حذف حدث التقويم المرتبط تلقائياً في الخلفية. يؤدي إلغاء موعد تم إلغاؤه بالفعل إلى إرجاع `400`.

| الحقل | مطلوب | الوصف |
|---|---|---|
| `cancellation_reason` | لا | سبب الإلغاء، يتم تخزينه مع الموعد. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cancellation_reason": "Client asked to reschedule next month"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      cancellation_reason: "Client asked to reschedule next month",
    }),
  }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"cancellation_reason": "Client asked to reschedule next month"},
)
print(res.json()["success"])
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678"
}
```

---

## حذف موعد

`DELETE /appointments/{appointmentId}`

يحذف الموعد ومراجعه نهائياً. إذا كنت ترغب فقط في إلغاء الحجز مع الاحتفاظ بالسجل، استخدم [إلغاء](#cancel-an-appointment) بدلاً من ذلك.

**cURL**

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

**JavaScript**

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

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
```

**الاستجابة** (`200 OK`):

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

---

## سرد تقويمات Google المرتبطة بك

`GET /appointments/google-calendars`

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

لا يعمل هذا إلا بعد ربط الحساب بـ Google Calendar (الإعدادات ← عمليات الربط) مع صلاحية القراءة على الأقل. إذا لم يتم ذلك، أو إذا لم يعد الوصول الممنوح يتضمن نطاق قراءة التقويم، فستتلقى `400` يطلب منك ربطه (أو إعادة ربطه).

**cURL**

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

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/google-calendars",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["data"])
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "data": [
    {
      "id": "primary",
      "summary": "jane@example.com",
      "timeZone": "America/New_York",
      "accessRole": "owner",
      "primary": true
    },
    {
      "id": "abcdefg1234567890@group.calendar.google.com",
      "summary": "Bookings",
      "timeZone": "America/New_York",
      "accessRole": "writer"
    }
  ]
}
```

كل إدخال هو عبارة عن شكل [`CalendarListEntry`](https://developers.google.com/calendar/api/v3/reference/calendarList) خاص بـ Google، لذا تتبع أسماء الحقول `camelCase` الخاصة بـ Google، وليس `snake_case` المعتادة لهذه الواجهة البرمجية — فهذه بيانات Google التي يتم تمريرها كما هي، وليست بياناتنا. يؤدي فقدان الاتصال أو إلغاؤه إلى إرجاع `400` مع خطأ يوضح الحاجة إلى ربط (أو إعادة ربط) Google Calendar.

---

## استيراد الأحداث من تقويم Google

`POST /appointments/import-calendar-events`

يقوم بسحب الأحداث الموجودة بالفعل في تقويم (تقاويم) Google المرتبطة بحملة أو وكيل ذكاء اصطناعي (AI Agent) ويحولها إلى مواعيد — وهو أمر مفيد في المرة الأولى التي تربط فيها تقويماً يحتوي بالفعل على حجوزات. قد يستغرق هذا بعض الوقت (حيث يمر كل حدث عبر عملية استخراج لمعرفة لمن يخص)، لذا فهو لا يعمل بشكل فوري: يضع الطلب مهمة في الخلفية ويعيد لك `job_id` للاستعلام عن حالتها.

| الحقل | مطلوب | الوصف |
|---|---|---|
| `campaign_id` | واحد من هذين الاثنين | الحملة التي سيتم الاستيراد من تقويمها (تقاويمها) المرتبط. |
| `agent_id` | واحد من هذين الاثنين | وكيل الذكاء الاصطناعي (AI Agent) الذي سيتم الاستيراد من تقويمه (تقاويمها) المرتبط. |
| `identifier` | نعم | `"EMAIL"` أو `"PHONE_NUMBER"` — أي جزء من معلومات الاتصال يجب استخراجه من كل حدث في التقويم لمطابقة جهة الاتصال التي ينتمي إليها أو إنشائها. |

أرسل واحداً فقط من `campaign_id` / `agent_id`، ولا ترسل كلاهما أبداً ولا تترك كلاهما فارغاً — أي من هاتين الحالتين ستؤدي إلى إرجاع `400`. يجب أن ينتمي أي منهما ترسله إلى حسابك، وإلا فستتلقى `404`.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/import-calendar-events?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_abc123",
    "identifier": "EMAIL"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments/import-calendar-events", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agent_id: "agent_abc123",
    identifier: "EMAIL",
  }),
});
const data = await res.json();
console.log(data.job_id);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/import-calendar-events",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"agent_id": "agent_abc123", "identifier": "EMAIL"},
)
print(res.json()["job_id"])
```

**الاستجابة** (`202 Accepted`):

```json
{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "queued",
  "campaign_id": null,
  "agent_id": "agent_abc123"
}
```

يعيد `campaign_id` و `agent_id` صدى أيهما أرسلت؛ والآخر يكون دائماً `null`.

### الاستعلام عن حالة مهمة الاستيراد

`GET /appointments/import-calendar-events/{jobId}`

```bash
curl "https://api.youraiconnector.com/v1/appointments/import-calendar-events/jK9mQ2xR7pL4wN1t" \
  -H "X-API-Key: YOUR_API_KEY"
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "completed",
  "message": "Imported 12 events as appointments.",
  "error": null
}
```

| `status` | المعنى |
|---|---|
| `queued` | لم يتم البدء بها بعد. استمر في الاستعلام. |
| `processing` | عملية الاستيراد قيد التشغيل. استمر في الاستعلام. |
| `completed` | تم الانتهاء — يحتوي `message` على ملخص قصير ومقروء. |
| `failed` | حدث خطأ ما — يحتوي `error` على السبب. |

يؤدي تنفيذ `GET` على `jobId` غير موجود (أو ينتمي إلى حساب مختلف) إلى إرجاع `404`.

---

## عمليات ربط حجوزات المطاعم (Zenchef / Formitable)

Zenchef و Formitable هما نظامان لحجوزات المطاعم يمكن لوكيل الذكاء الاصطناعي (AI Agent) الخاص بك حجز طاولات حقيقية من خلالهما. لكل منهما **أداة حجز عامة وغير مصادق عليها** (`https://api.youraiconnector.com/v1/zenchef-widget/...` و `https://api.youraiconnector.com/v1/formitable-widget/...`) تظهر داخل الدردشة للعميل — مسارات الأداة تلك هي صفحات HTML عادية مخصصة للفتح في المتصفح، وليست نقاط نهاية لواجهة برمجة تطبيقات JSON، لذا فهي غير موثقة هنا. ما يلي هو نقاط نهاية إدارة الحساب: التحقق من أن معرف المطعم ينتمي إلى صاحب الحساب، ثم إضافته أو تحديثه أو إزالته.

### Zenchef

يعد ربط مطعم Zenchef عملية تحقق من خطوتين، حيث يثبت صاحب الحساب أنه يدير المطعم فعلياً قبل ربطه بالبوت: أولاً، يتم التحقق من وجود المعرف (دون الكشف عن الاسم)، ثم يُطلب منهم كتابة اسم المطعم بأنفسهم للتحقق من تطابقه.

**الخطوة 1 — التحقق من وجود معرف المطعم**

`POST /appointments/zenchef-restaurants/check`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `restaurant_id` | نعم | معرف مطعم Zenchef المراد التحقق منه. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/check?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345" }'
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "data": { "exists": true, "requiresNameVerification": true }
}
```

تعني `exists: false` أنه لا يوجد مطعم Zenchef بهذا المعرف — لا يوجد شيء آخر للقيام به. الحد المسموح به هو 10 عمليات تحقق لكل 5 دقائق لكل حساب؛ تجاوز هذا الحد يؤدي إلى إرجاع `429`.

**الخطوة 2 — التحقق من اسم المطعم**

`POST /appointments/zenchef-restaurants/verify-name`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `restaurant_id` | نعم | معرف مطعم Zenchef من الخطوة 1. |
| `user_input_name` | نعم | الاسم الذي كتبه صاحب الحساب — تتم مقارنته بالاسم الحقيقي للمطعم على Zenchef (غير حساس لحالة الأحرف/المسافات). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/verify-name?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "user_input_name": "The Blue Door Bistro" }'
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "id": "12345",
      "name": "The Blue Door Bistro",
      "address": "1 Rue de Rivoli, Paris",
      "status": "active"
    }
  }
}
```

تعني `verified: false` أن الاسم غير متطابق — يتم حذف `restaurantDetails`، اطلب من صاحب الحساب المحاولة مرة أخرى. الحد المسموح به هو 3 محاولات لكل 5 دقائق (أكثر صرامة من التحقق من الوجود، لأن هذه هي خطوة الإثبات الفعلية). يؤدي استخدام `restaurant_id` لم يعد متاحاً على Zenchef إلى إرجاع `404`.

**الخطوة 3 — حفظ المطعم**

`POST /appointments/zenchef-restaurants`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `restaurant_id` | نعم | 1–64 حرفاً، أحرف/أرقام/شرطة سفلية/واصلة. |
| `restaurant_name` | نعم | اسم المطعم الذي تم التحقق منه من الخطوة 2. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "restaurant_name": "The Blue Door Bistro" }'
```

**الاستجابة** (`201 Created`):

```json
{ "success": true, "data": { "restaurantId": "12345" } }
```

**تحديث مطعم Zenchef محفوظ**

`PUT /appointments/zenchef-restaurants/{restaurantId}`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `restaurant_name` | لا | اسم العرض الجديد. |
| `is_active` | لا | اضبط `false` لإيقاف البوت عن الحجز في هذا المطعم دون إزالته. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**الاستجابة** (`200 OK`): نفس شكل استجابة الحفظ أعلاه.

**إزالة مطعم Zenchef**

`DELETE /appointments/zenchef-restaurants/{restaurantId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY"
```

**الاستجابة** (`200 OK`): `{ "success": true, "data": { "restaurantId": "12345" } }`

المطعم `restaurantId` غير الموجود حالياً في الحساب يُرجع `404` عند التحديث أو الحذف.

### Formitable

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

**التحقق من معرف المطعم**

`POST /appointments/formitable-restaurants/verify`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `restaurant_id` | نعم | معرف مطعم Formitable. |
| `language` | لا | علامة اللغة لطلب الفحص. القيمة الافتراضية هي `"nl"`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/verify?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "the-blue-door", "language": "en" }'
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "restaurantId": "the-blue-door",
      "productCount": 4,
      "sampleProductTitle": "Dinner for two",
      "language": "en"
    }
  }
}
```

إرجاع `restaurant_id` لا يتعرف عليه Formitable يؤدي إلى `404`. يخضع لمعدل محدد بـ 10 محاولات كل 5 دقائق لكل حساب.

**الحصول على تفاصيل المطعم**

`GET /appointments/formitable-restaurants/{restaurantId}/details?language=en`

يجلب الملف الشخصي العام للمطعم من Formitable، بما في ذلك موقعه الإلكتروني — يُستخدم لتخزين عنوان URL الخاص بالموقع مؤقتاً أثناء إعداد المطعم. `language` هو معامل استعلام اختياري، وقيمته الافتراضية هي `"en"`.

```bash
curl "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door/details?language=en" \
  -H "X-API-Key: YOUR_API_KEY"
```

**الاستجابة** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "uid": "the-blue-door",
    "name": "The Blue Door Bistro",
    "website": "https://thebluedoorbistro.com",
    "email": "info@thebluedoorbistro.com",
    "telephone": "+31201234567",
    "streetAddress": "Prinsengracht 1",
    "zipcode": "1015 AB",
    "city": "Amsterdam",
    "country": "Netherlands",
    "countryCode": "NL",
    "currency": "EUR"
  }
}
```

**حفظ المطعم**

`POST /appointments/formitable-restaurants`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `restaurant_id` | نعم | 1–64 حرفاً، أحرف/أرقام/شرطة سفلية/واصلة. |
| `restaurant_name` | نعم | اسم العرض. |
| `language` | نعم | وسم لغة ISO، على سبيل المثال `"en"` أو `"en-GB"`. |
| `website_url` | لا | الموقع الإلكتروني للمطعم، من بحث التفاصيل أعلاه. يجب أن يكون `http(s)://`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "restaurant_id": "the-blue-door",
    "restaurant_name": "The Blue Door Bistro",
    "language": "en",
    "website_url": "https://thebluedoorbistro.com"
  }'
```

**الاستجابة** (`201 Created`): `{ "success": true, "data": { "restaurantId": "the-blue-door" } }`

**تحديث مطعم Formitable محفوظ**

`PUT /appointments/formitable-restaurants/{restaurantId}`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `restaurant_name` | لا | اسم العرض الجديد. |
| `language` | لا | وسم لغة ISO جديد. |
| `is_active` | لا | اضبط `false` لإيقاف البوت عن الحجز في هذا المطعم دون إزالته. |
| `website_url` | لا | رابط الموقع الإلكتروني الجديد. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**الاستجابة** (`200 OK`): نفس شكل استجابة الحفظ أعلاه.

**إزالة مطعم Formitable**

`DELETE /appointments/formitable-restaurants/{restaurantId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY"
```

**الاستجابة** (`200 OK`): `{ "success": true, "data": { "restaurantId": "the-blue-door" } }`

المطعم `restaurantId` غير الموجود حالياً في الحساب يُرجع `404` عند التحديث أو الحذف.

> **شكل الخطأ في جميع نقاط نهاية Zenchef/Formitable:** على عكس بقية هذه الصفحة، تحمل الأخطاء هنا حالتها مرتين — مرة كحالة HTTP ومرة كـ `error_code` في النص الأساسي — على سبيل المثال `{ "success": false, "error": "Restaurant not found", "error_code": 404 }`. تعامل معها بنفس الطريقة التي تتعامل بها مع أي خطأ آخر: تحقق من `success`، واقرأ `error` للحصول على الرسالة.

---

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

تُرجع نقاط نهاية المواعيد غلاف الخطأ القياسي:

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

| الحالة | متى يحدث ذلك في نقطة نهاية المواعيد |
|---|---|
| `400` | حقل مطلوب مفقود أو غير صالح — على سبيل المثال `start_time` سيئ، أو `end_time` ليس بعد `start_time`، أو تركيبة عوامل تصفية غير صالحة، أو عدم وجود حقول للتحديث، أو موعد تم إلغاؤه بالفعل. |
| `404` | لم يتم العثور على الموعد أو جهة الاتصال أو نوع الحدث. |
| `409` | الفترة الزمنية المطلوبة محجوزة بالفعل (تعارض في الحجز). |

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

---

## الخطوات التالية

- [جهات الاتصال](contacts.md) — إنشاء جهات الاتصال التي تحجز لها والبحث عنها.
- [الرسائل والمحادثات](messages.md) — إرسال تأكيد أو تذكير إلى جهة اتصال.
- [خطافات الويب (Webhooks)](webhooks.md) — تلقي إشعارات عند تغيير المواعيد.
