
# واجهة برمجة تطبيقات خطافات الويب (Webhooks API)

تسمح خطافات الويب (Webhooks) للمنصة بإخطار أنظمتك الأخرى في اللحظة التي يحدث فيها شيء ما — كإضافة جهة اتصال جديدة، أو تلقي رد، أو حجز موعد، والمزيد. تدير واجهة برمجة التطبيقات هذه **الاشتراكات** نفسها: أي عناوين URL تتلقى أي أحداث. لمعرفة كيفية تلقي والتحقق من الحمولات (payloads) التي تحصل عليها نقطة النهاية الخاصة بك، راجع [خطافات الويب](../integrations/webhooks.md).

جميع المسارات أدناه نسبية إلى عنوان URL الأساسي لواجهة برمجة التطبيقات:

```
https://api.youraiconnector.com/v1
```

يجب مصادقة كل طلب. راجع [المصادقة](authentication.md) لمعرفة الطرق الأربع المقبولة. تستخدم الأمثلة هنا رأس `X-API-Key` (ونموذج معلمة استعلام واحد لـ cURL).

::: note
**ملاحظة:** يجب تفعيل خطافات الويب (Webhooks) لحسابك. إذا لم تكن مفعلة، فستعيد نقاط النهاية هذه `403`.
:::


---

## كيفية عنونة الاشتراكات

لكل اشتراك `id` و `name` اختياري. يمكن استخدام أي منهما كـ `{webhookId}` في المسار للتحديث، أو الحذف، أو الاختبار، أو التحقق من الحالة، أو إعادة التفعيل.

> **يفضل استخدام الاسم.** معرفات الاشتراكات تعتمد على الموقع، لذا يمكن أن تتغير بعد حذف اشتراك آخر. إذا قمت بتعيين `name` ثابت عند إنشاء اشتراك، فقم بعنونته بالاسم لتجنب المفاجآت.

---

## سرد الاشتراكات

`GET /webhooks`

**cURL**

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

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/webhooks",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

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

```json
{
  "success": true,
  "webhooks": [
    {
      "id": "0",
      "name": "Order updates hook",
      "url": "https://hooks.example.com/incoming",
      "subscribed_to": ["Contact Created", "Replies"],
      "subscribed_to_tags": [],
      "created_at": "2026-06-09T12:00:00.000Z",
      "signing_enabled": true,
      "signing_secret_created_at": "2026-07-15T09:30:00.000Z",
      "retries_enabled": true,
      "enabled": true,
      "apply_to_sub_accounts": false
    }
  ]
}
```

`signing_enabled` و `retries_enabled` هما خياران لكل اشتراك، وكلاهما معطل ما لم تقم بتفعيلهما. راجع [الحمولات الموقعة](#signed-payloads) و[إعادة المحاولات](#retries).

`apply_to_sub_accounts` هو خيار الاشتراك في وراثة الوكالة — راجع [اشتراك واحد لجميع حسابات العملاء](#one-subscription-for-all-client-accounts-agencies). يكون معطلاً افتراضياً، وغير فعال في الحسابات التي لا تملك حسابات عملاء.

`enabled` هو مفتاح التشغيل/الإيقاف الخاص بالاشتراك — راجع [إيقاف الاشتراك](#switching-a-subscription-off). تظل الاشتراكات التي تم إيقافها مدرجة هنا.

لا يتم تضمين سر التوقيع نفسه هنا مطلقًا — اقرأه من [`GET /webhooks/{id}/signing-secret`](#read-the-signing-secret).

---

## سرد أنواع الأحداث القابلة للاشتراك

تُرجع السلاسل النصية الدقيقة التي يمكنك استخدامها في `subscribed_to`. استخدم هذا لاكتشاف أسماء الأحداث الصالحة بدلاً من كتابتها برمجياً بشكل ثابت.

`GET /webhooks/events`

**cURL**

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

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/webhooks/events",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

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

الاستجابة هي `{"success": true, "events": [...]}`، حيث تحتوي `events` حالياً على 22 سلسلة نصية دقيقة: Contact Created، Human Alerted، Appointment Booked، Replies، Reads، Deliveries، Credits Spent، Credits Recharged، Low Credit Balance، Contact Paused، Contact Do Not Disturb، Contact Unarchived، New Message، Contact Resumed، Chat Concluded، Task Created، Task Updated، Task Completed، Daily Summary Created، Channel Connected، Broadcast Started، و Broadcast Completed (يتم قبول Channel Connected في `subscribed_to` ولكن لا يوجد ما يصدره حالياً، لذا لا تعتمد عليه في بنائك).

لمعرفة معنى كل حدث ورمز `event` الذي يرسله في الحمولة (payload)، راجع [أحداث الـ Webhook الـ 22](../integrations/webhooks.md#the-22-webhook-events). نقطة النهاية هذه هي القائمة المعتمدة في أي لحظة — اقرأها مباشرة بدلاً من كتابة الأسماء برمجياً (hard-coding).

---

## إنشاء اشتراك

`POST /webhooks`

| الحقل | مطلوب | الوصف |
|---|---|---|
| `url` | نعم | عنوان HTTPS الذي سيستقبل حمولات الأحداث عبر `POST`. يجب أن يكون متاحاً للوصول من الجمهور. |
| `subscribed_to` | نعم | مصفوفة غير فارغة من أسماء الأحداث (راجع `/webhooks/events`). |
| `name` | لا | اسم للعرض. يمكن استخدامه أيضاً كـ `{webhookId}` لاحقاً. يتم تعيينه افتراضياً كاسم يحمل طابعاً زمنياً. |
| `subscribed_to_tags` | لا | معرفات الوسوم (Tag IDs) التي تحدد الوسوم التي تنتج إشعاراً بملخص المحادثة. لا يقتصر هذا على أحداث الاشتراك لتلك الوسوم — للحصول على طلب عند تطبيق وسم معين، قم بتعيين عنوان URL للـ webhook على ذلك الوسم في علامة التبويب **الوسوم** (Tags) الخاصة بالوكيل (أو الحملة). |
| `retries_enabled` | لا | قيمة منطقية (Boolean)، الافتراضي هو `false`. اختر الاشتراك في [إعادة المحاولة](#retries) لعمليات التسليم الفاشلة. |
| `generate_signing_secret` | لا | قيمة منطقية (Boolean)، الافتراضي هو `false`. قم بإنشاء [سر توقيع](#signed-payloads) HMAC مع الاشتراك. يتم إرجاع السر مرة واحدة، كـ `signing_secret` في المستوى الأعلى من الاستجابة. |
| `enabled` | لا | قيمة منطقية (Boolean)، الافتراضي هو `true`. مرر `false` لإنشاء الاشتراك وهو في حالة إيقاف. راجع [إيقاف الاشتراك](#switching-a-subscription-off). |
| `apply_to_sub_accounts` | لا | قيمة منطقية (Boolean)، الافتراضي هو `false`. في حساب الوكالة، يجعل `true` هذا الاشتراك يستقبل أيضاً أحداثاً من كل حساب عميل — راجع [اشتراك واحد لجميع حسابات العملاء](#one-subscription-for-all-client-accounts-agencies). |

> **قواعد URL:** يجب أن يستخدم عنوان URL بروتوكول `https://` وأن يكون قابلاً للوصول من الشبكة العامة. يتم رفض عناوين `http://` العادية، و`localhost`، وعناوين الشبكات الخاصة، وعناوين الشبكات الداخلية للمنصة مع إرجاع `400`.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/webhooks?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/incoming",
    "subscribed_to": ["Contact Created", "Replies"],
    "name": "Order updates hook"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/webhooks", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://hooks.example.com/incoming",
    subscribed_to: ["Contact Created", "Replies"],
    name: "Order updates hook",
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/webhooks",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "url": "https://hooks.example.com/incoming",
        "subscribed_to": ["Contact Created", "Replies"],
        "name": "Order updates hook",
    },
)
data = res.json()
```

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

```json
{
  "success": true,
  "webhook_id": "1",
  "webhook": {
    "id": "1",
    "name": "Order updates hook",
    "url": "https://hooks.example.com/incoming",
    "subscribed_to": ["Contact Created", "Replies"],
    "subscribed_to_tags": [],
    "created_at": "2026-06-09T12:00:00.000Z"
  }
}
```

---

## تحديث اشتراك

قدم واحداً على الأقل من `url`، أو `subscribed_to`، أو `name`، أو `subscribed_to_tags`، أو `retries_enabled`، أو `enabled`، أو `apply_to_sub_accounts`. الحقول المحذوفة تحتفظ بقيمها الحالية. `subscribed_to` و `subscribed_to_tags` هما استبدالات، وليسا دمجاً.

`PUT /webhooks/{webhookId}`

> لا يؤدي تحديث الاشتراك أبدًا إلى تعطيل سر التوقيع الخاص به — قم بإدارة ذلك من خلال [مسارات سر التوقيع](#signed-payloads).

> عند تغيير عنوان URL، يتم إعادة تفعيل التسليم للعنوان الجديد تلقائياً، مما يمنح نقطة النهاية التي كانت تفشل سابقاً بداية جديدة.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/webhooks/Order%20updates%20hook" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/v2/incoming",
    "subscribed_to": ["Replies", "Chat Concluded"]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  `https://api.youraiconnector.com/v1/webhooks/${encodeURIComponent("Order updates hook")}`,
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://hooks.example.com/v2/incoming",
      subscribed_to: ["Replies", "Chat Concluded"],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/webhooks/Order updates hook",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "url": "https://hooks.example.com/v2/incoming",
        "subscribed_to": ["Replies", "Chat Concluded"],
    },
)
data = res.json()
```

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

```json
{
  "success": true,
  "webhook_id": "0",
  "webhook": {
    "id": "0",
    "name": "Order updates hook",
    "url": "https://hooks.example.com/v2/incoming",
    "subscribed_to": ["Replies", "Chat Concluded"],
    "subscribed_to_tags": [],
    "created_at": "2026-06-09T12:00:00.000Z"
  }
}
```

المعرف أو الاسم غير المعروف يؤدي إلى إرجاع `404` مع `{ "success": false, "error": "Webhook not found" }`.

---

## حذف اشتراك

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

`DELETE /webhooks/{webhookId}`

**cURL**

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

**JavaScript**

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

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/webhooks/0",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

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

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

---

## إرسال حمولة اختبار

يرسل حمولة نموذجية إلى عنوان URL الخاص بالاشتراك حتى تتمكن من التحقق من جهاز الاستقبال الخاص بك من البداية إلى النهاية. يمكنك اختيارياً تمرير `event` للتحكم في نوع الحدث الذي تحاكيه العينة. عمليات تسليم الاختبار لا تؤثر أبداً على عدادات سلامة الاشتراك.

`POST /webhooks/{webhookId}/test`

تُرجع الاستجابة دائماً `200` وتُبلغ عن النتيجة باستخدام علامة `delivered` — الاختبار الفاشل **لا** يُرجع حالة خطأ. عندما تكون `delivered` مساوية لـ `false`، تتضمن الاستجابة تفاصيل الفشل.

| الحقل | مطلوب | الوصف |
|---|---|---|
| `event` | لا | نوع الحدث المراد محاكاته (يجب أن يكون واحداً من `/webhooks/events`). القيمة الافتراضية هي حدث تسليم. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/webhooks/0/test?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "event": "Contact Created" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/webhooks/0/test", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ event: "Contact Created" }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/webhooks/0/test",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"event": "Contact Created"},
)
data = res.json()
```

**الاستجابة** (تم التسليم)

```json
{
  "success": true,
  "webhook_id": "0",
  "delivered": true
}
```

**الاستجابة** (فشل التسليم)

```json
{
  "success": true,
  "webhook_id": "0",
  "delivered": false,
  "failure_type": "permanent",
  "status_code": 404,
  "error_message": "Request failed with status code 404"
}
```

`failure_type` هي واحدة من `permanent`، أو `temporary`، أو `timeout`، أو `network`، أو `unknown`.

---

## التحقق من سلامة التسليم

يعيد سجل سلامة التسليم الخاص بعنوان URL الخاص بالاشتراك: عدد عمليات التسليم التي نجحت والتي فشلت، وما إذا كان التسليم متوقفاً مؤقتاً حالياً بعد فشل متكرر، وتفاصيل آخر فشل. يعيد `"health": null` عندما لم تتم محاولة أي عمليات تسليم بعد.

`GET /webhooks/{webhookId}/health`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/webhooks/0/health" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/webhooks/0/health",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

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

```json
{
  "success": true,
  "webhook_id": "0",
  "url": "https://hooks.example.com/incoming",
  "health": {
    "consecutive_failures": 0,
    "total_failures": 2,
    "total_successes": 120,
    "is_disabled": false,
    "disabled_at": null,
    "disabled_reason": null,
    "last_failure": null,
    "last_success_at": "2026-06-09T12:00:00.000Z",
    "created_at": "2026-05-01T08:00:00.000Z",
    "updated_at": "2026-06-09T12:00:00.000Z"
  }
}
```

عندما تكون `is_disabled` هي `true`، فهذا يعني أنه تم إيقاف التسليم إلى عنوان URL تلقائياً بعد فشل متكرر. قم بإصلاح جهاز الاستقبال الخاص بك، ثم أعد تمكينه (أدناه).

---

## إعادة تمكين التسليم

يستأنف التسليم لخطاف ويب (webhook) تم إيقاف عنوان URL الخاص به تلقائياً بعد فشل متكرر. يؤدي هذا إلى إعادة تعيين علامة الإيقاف المؤقت وعدادات الفشل ولكنه **لا** يحاول إجراء عملية تسليم — استخدم نقطة نهاية الاختبار بعد ذلك للتأكد من أن جهاز الاستقبال الخاص بك يعمل بشكل سليم مرة أخرى.

`POST /webhooks/{webhookId}/reenable`

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/webhooks/0/reenable?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/webhooks/0/reenable", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/webhooks/0/reenable",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

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

```json
{
  "success": true,
  "webhook_id": "0"
}
```

---

## إيقاف الاشتراك

`enabled` هو مفتاح التشغيل/الإيقاف الخاص بالاشتراك. يؤدي إيقافه إلى إيقاف عمليات التسليم مع الحفاظ على عنوان URL وقائمة الأحداث وسر التوقيع كما هي.

```bash
# Off
curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

# Back on
curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'
```

- **الغياب يعني التشغيل.** الاشتراك الذي تم إنشاؤه قبل وجود هذا الحقل لا يحتوي على قيمة `enabled` مخزنة ويعمل بشكل طبيعي. يقوم `GET /webhooks` دائماً بالإبلاغ عن قيمة منطقية محددة.
- تظل الاشتراكات التي تم إيقافها **مدرجة** بواسطة `GET /webhooks` — وهذه هي الطريقة التي تعثر بها عليها لإعادة تشغيلها.
- [إعادة المحاولة](#retries) التي تم وضعها في قائمة الانتظار قبل الإيقاف لا تُستأنف: تعيد عملية إعادة المحاولة قراءة الاشتراك في وقت الإرسال وتتجاهله إذا كان متوقفاً.
- لا يتم إعادة تشغيل أي شيء تم كبته أثناء الإيقاف عند إعادة تشغيله مرة أخرى.

> يختلف هذا عن التعطيل التلقائي بعد الإخفاقات المتكررة، والذي يتم الإبلاغ عنه بواسطة [`GET /webhooks/{id}/health`](#check-delivery-health) كـ `is_disabled` ويتم مسحه باستخدام [`POST /webhooks/{id}/reenable`](#re-enable-delivery). `enabled` هو مفتاح الحساب؛ و `is_disabled` هو مفتاحنا. لا يلغي أحدهما الآخر — يجب أن يكون الاشتراك قيد التشغيل وغير معطل تلقائياً ليتم التسليم.

---

## اشتراك واحد لجميع حسابات العملاء (الوكالات)

في حساب الوكالة، قم بتعيين `apply_to_sub_accounts: true` على اشتراك (عند وقت الإنشاء أو عبر `PUT`) وسوف يستقبل أيضاً الأحداث التي تقع في كل حساب من حسابات عملاء الوكالة — نقطة نهاية واحدة تغطي الوكالة بأكملها، بدلاً من إعادة إنشاء الاشتراك في كل حساب عميل.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"apply_to_sub_accounts": true}'
```

كيف يعمل:

- **كتلة `user` تميز الحسابات عن بعضها.** تحدد كتلة `user` في كل حمولة الحساب الذي وقع فيه الحدث فعلياً، بحيث يمكن للمستقبل الخاص بك التوجيه لكل عميل.
- **إعدادات اشتراك الوكالة نفسها تنطبق في كل مكان.** قائمة الأحداث الخاصة به، و[سر التوقيع](#signed-payloads)، وخيار [إعادة المحاولة](#retries) تُستخدم أيضاً لعمليات التسليم الموروثة.
- **اشتراك حساب العميل نفسه لنفس عنوان URL هو الذي له الأولوية.** إذا كان لدى حساب العميل اشتراكه الخاص الذي يشير إلى نفس عنوان URL، فسيتم استخدام ذلك الاشتراك لأحداث ذلك الحساب — لا يتم تسليم نفس الحدث مرتين لنفس نقطة النهاية.
- **حسابات العملاء لا تراه.** الاشتراكات الموروثة لا تظهر في قائمة الـ webhook الخاصة بحساب العميل، ولا يمكن للعميل إيقافها — الوكالة فقط هي التي تديرها.
- **يتم تتبع سلامة التسليم لكل حساب عميل.** نقطة النهاية التي تستمر في الفشل يتم تعطيلها تلقائياً للحساب الذي فشلت عمليات تسليمه، وليس للوكالة بأكملها.
- **`subscribed_to_tags` لا يتم توريثه.** تشير قائمة الوسوم إلى وسوم الوكالة نفسها، والتي لا وجود لها في حسابات العملاء — تضييق نطاق ملخص المحادثة ينطبق فقط على أحداث الوكالة نفسها.
- **غير فعال في أماكن أخرى.** في حساب لا يملك حسابات عملاء، يتم تخزين العلم بشكل سليم ولا يقوم بأي إجراء.

---

## الترويسات في كل عملية تسليم

يتم إرسال هذه الترويسات الثلاث في كل عملية تسليم، سواء كان الاشتراك موقعاً أم لا:

| الترويسة | المعنى |
|---|---|
| `X-Webhook-Delivery` | معرف ثابت للحدث المنطقي. متطابق عبر عمليات إعادة المحاولة — استخدمه لإلغاء التكرار. |
| `X-Webhook-Attempt` | رقم المحاولة (يبدأ من 1). |
| `X-Webhook-Event` | اسم الحدث. |

---

## الحمولات الموقعة

التوقيع اختياري، ومتوقف افتراضياً، ويتم تعيينه لكل اشتراك. عندما يحتوي الاشتراك على سر توقيع، تحمل كل عملية تسليم ترويستين إضافيتين فوق الترويسات الثلاث المرسلة في كل عملية تسليم (`X-Webhook-Delivery`، `X-Webhook-Attempt`، و `X-Webhook-Event`):

| الترويسة | المعنى |
|---|---|
| `X-Webhook-Signature` | `v1=<hex>` — خوارزمية HMAC-SHA256 للسلسلة `"<timestamp>.<raw request body>"`، مشفرة باستخدام سر التوقيع الخاص بكل ويب هوك والذي تقوم بإنشائه وتدويره في `GET/POST/DELETE /v1/webhooks/{webhookId}/signing-secret`. |
| `X-Webhook-Timestamp` | وقت الإرسال، بالثواني وفق نظام Unix. مرتبط بالتوقيع، لذا لا يمكن تغييره بشكل مستقل. |

للتحقق، أعد حساب HMAC-SHA256 فوق النص الخام (raw body) باستخدام سرك وقارنه بالترويسة. تحقق مقابل نص الطلب **الخام**. إعادة تسلسل JSON الذي تم تحليله يغير البايتات ويكسر المقارنة. ارفض عمليات التسليم التي يكون طابعها الزمني خارج نافذة الصلاحية (300 ثانية هو افتراضي معقول) لمنع إعادة التشغيل، وقارن باستخدام دالة آمنة زمنياً. |

راجع [الحمولات الموقعة](../integrations/webhooks.md#signed-payloads-verifying-a-webhook-really-came-from-us) للحصول على أمثلة كاملة للتحقق باستخدام Node و Python.

> **التوقيع ليس هو نفسه مصادقة واجهة برمجة التطبيقات (API).** واجهة برمجة تطبيقات REST نفسها تتم مصادقتها باستخدام مفاتيح API بدلاً من OAuth (يوجد OAuth 2.1 لخوادم MCP التي تسجلها كأدوات بوت)، ولا توجد حزم SDK رسمية على npm أو PyPI حتى الآن — اتصل بالنهايات الطرفية باستخدام أي عميل HTTP.

### قراءة سر التوقيع

`GET /webhooks/{id}/signing-secret`

```bash
curl "https://api.youraiconnector.com/v1/webhooks/0/signing-secret?apiKey=YOUR_API_KEY"
```

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

```json
{
  "success": true,
  "webhook_id": "0",
  "signing_enabled": true,
  "signing_secret": "whsec_1a2b3c...",
  "signing_secret_created_at": "2026-07-15T09:30:00.000Z"
}
```

عندما يكون التوقيع معطلاً، يكون `signing_enabled` هو `false` و `signing_secret` هو `null`.

### إنشاء أو تدوير سر التوقيع

`POST /webhooks/{id}/signing-secret`

ينشئ سراً (مع تفعيل التوقيع) أو يستبدل السراً الحالي. يُرجع السراً الجديد.

```bash
curl -X POST "https://api.youraiconnector.com/v1/webhooks/0/signing-secret?apiKey=YOUR_API_KEY"
```

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

```json
{
  "success": true,
  "webhook_id": "0",
  "signing_enabled": true,
  "signing_secret": "whsec_9f8e7d...",
  "signing_secret_created_at": "2026-07-15T10:00:00.000Z"
}
```

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

يمكنك أيضاً إنشاء سراً عند الإنشاء عن طريق تمرير `"generate_signing_secret": true` إلى `POST /webhooks`؛ تتضمن الاستجابة بعد ذلك حقلاً من المستوى الأعلى `signing_secret`.

### إيقاف التوقيع

`DELETE /webhooks/{id}/signing-secret`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/webhooks/0/signing-secret?apiKey=YOUR_API_KEY"
```

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

```json
{
  "success": true,
  "webhook_id": "0",
  "signing_enabled": false
}
```

> تتطلب جميع مسارات سر التوقيع الثلاثة إذن **تعديل** (edit) الخاص بالتكاملات، بما في ذلك `GET` — السراً هو بيانات اعتماد يمكنها تزوير عمليات التسليم، لذا لا يتم كشفه للأدوار ذات صلاحية القراءة فقط.

---

## عمليات إعادة المحاولة

اختياري، معطل افتراضياً، ويتم ضبطه لكل اشتراك عبر القيمة المنطقية `retries_enabled` في `POST /webhooks` أو `PUT /webhooks/{id}`.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"retries_enabled": true}'
```

عند التفعيل، تتم إعادة محاولة التسليم الفاشل في غضون **دقيقة واحدة، 5 دقائق، 30 دقيقة، وساعتين** بعد المحاولة الأولى (حوالي ساعتين و40 دقيقة من التغطية).

- **تمت إعادة المحاولة:** استجابات 5xx، انتهاء المهلة، وفشل الاتصال.
- **لم تتم إعادة المحاولة:** أي استجابة 4xx. المتلقي يرفض الطلب نفسه، لذا فإن إعادة تشغيله دون تغيير لن يؤدي إلا إلى تكرار الرفض.

تجعل عمليات إعادة المحاولة تسليم البيانات المكررة أمراً ممكناً — نقطة النهاية التي عالجت حدثاً ولكن انتهت مهلتها قبل الاستجابة ستراه مرة أخرى. قم بإلغاء التكرار بناءً على `X-Webhook-Delivery`، والذي يظل ثابتاً عبر المحاولات. ولهذا السبب فإن عمليات إعادة المحاولة اختيارية.

تحسب عدادات [delivery-health](#check-delivery-health) عملية تسليم كاملة، وليس كل محاولة: يتم تسجيل الفشل مرة واحدة فقط بعد استنفاد كل محاولة إعادة، لذا فإن تفعيل عمليات إعادة المحاولة لا يؤدي إلى تشغيل التعطيل التلقائي بشكل أسرع.

---

## الأخطاء

تستخدم جميع الأخطاء الغلاف القياسي:

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

الحالات الشائعة: عنوان URL غير مسموح به، أو `subscribed_to` فارغ/غير صالح، أو حقول مفقودة تعيد `400`؛ معرف أو اسم غير معروف يعيد `404`؛ و `403` تعني أن خطافات الويب غير ممكّنة لحسابك. راجع [الأخطاء](errors-and-pagination.md) للحصول على القائمة الكاملة.

---

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

- [خطافات الويب (استقبال الحمولات)](../integrations/webhooks.md) — قم بإعداد جهاز الاستقبال الخاص بك وافهم شكل الحمولة.
- [المصادقة](authentication.md) — الطرق الأربع لمصادقة الطلب.
- [الأخطاء وحدود المعدل](errors-and-pagination.md) — رموز الحالة وحد 300 طلب/دقيقة.
