> ## Documentation Index
> Fetch the complete documentation index at: https://docs.karzoun.chat/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> اشترك في أحداث منصة كرزون — المحادثات، العملاء، تدفقات واتساب، المهام، والمزيد.

تُسلّم webhooks كرزون إشعارات HTTP فورية عند حدوث تغيير في مساحة عملك. سجّل نقطة نهاية HTTPS، واختر الأحداث التي تهمك، وترسل كرزون POST بحمولة JSON منظّمة كلما أُطلقت — مثلاً عند تعيين محادثة، أو تحديث عميل، أو اكتمال تدفق واتساب.

استخدم webhooks لمزامنة بيانات CRM، أو تشغيل أتمتة في بنيتك، أو إخطار أنظمة خارجية، أو تغذية تدفقات الأحداث إلى مسار التحليلات لديك.

<Info>
  **MiniApp مقابل webhooks مساحة العمل** — يغطي هذا الدليل webhooks **الصادرة** (كرزون تُخطر خادمك). لـ webhooks المزود **الواردة** المُعدّة داخل JSON لـ MiniApp، راجع [Webhooks في MiniApps](/ar/miniapps/guides/webhooks).
</Info>

<Info>
  **أين تدير webhooks** — أنشئ واطّلع على الاشتراكات في **المطوّر ← Webhooks** (`/developer/webhooks`) أو عبر طفرات GraphQL. ابحث في [مرجع GraphQL API](/api-reference) عن `Webhook` و`webhooksAdd` والعمليات ذات الصلة.
</Info>

## كيف يعمل التسليم

عند وقوع حدث، تقوم كرزون بـ:

1. إيجاد كل webhook **نشط** مشترك في زوج `type` + `action` ذلك
2. بناء غلاف JSON بإصدار (انظر [تنسيق الحمولة](#payload-format))
3. توقيع الجسم الخام وإرفاق ترويسات المصادقة
4. إرسال POST إلى عنوانك مع حتى **3 محاولات إعادة** عند الفشل
5. تسجيل كل محاولة في **سجلات التسليم** للتصحيح

يجب أن تعيد نقطة نهايتك أي حالة **2xx** ضمن مهلة الاتصال. الاستجابات غير 2xx وأخطاء الشبكة تشغّل إعادة المحاولة التلقائية مع تأخير أسي.

## الأمان

تأمّن كرزون حركة webhooks بـ **آليتين مستقلتين** في كل تسليم. استخدم واحدة أو كلتيهما للتأكد من أن الطلب جاء فعلاً من كرزون.

| الآلية         | الترويسة                  | الغرض                                                                             |
| -------------- | ------------------------- | --------------------------------------------------------------------------------- |
| **الرمز**      | `Karzoun-token`           | رمز حامل ثابت يُنشأ عند إنشاء الـ webhook                                         |
| **توقيع HMAC** | `X-Karzoun-Signature-256` | `sha256=` + HMAC-SHA256 لـ **جسم الطلب الخام** باستخدام `secret` الخاص بـ webhook |

خلال فترة سماح تدوير السر (24 ساعة عبر `webhooksRotateSecret`)، ترسل كرزون أيضاً `X-Karzoun-Signature-256-Previous` موقّعاً بالسر السابق حتى تتمكّن من التدوير دون توقف.

<Warning>
  تحقق دائماً من الرمز أو التوقيع قبل معالجة الحمولة. إذا لم يطابق أي منهما، أجب بـ `401` وتجاهل الجسم.
</Warning>

### التحقق من توقيع HMAC

قيمة ترويسة التوقيع مسبوقة بـ `sha256=`. انزع تلك البادئة، ثم قارن الباقي بـ HMAC تحسبه على **بايتات الجسم الخام بالضبط** (وليس كائن JSON أُعيد تسلسله).

<CodeGroup>
  ```js Node.js theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const crypto = require("crypto");

  function verifyKarzounWebhook(req, secret) {
    const header = req.headers["x-karzoun-signature-256"];
    if (!header || !header.startsWith("sha256=")) return false;

    const expected = header.slice("sha256=".length);
    const computed = crypto
      .createHmac("sha256", secret)
      .update(req.rawBody, "utf8") // use the raw body string/buffer
      .digest("hex");

    return crypto.timingSafeEqual(
      Buffer.from(expected, "hex"),
      Buffer.from(computed, "hex"),
    );
  }
  ```

  ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}}
  $secret = getenv('WEBHOOK_SECRET');
  $header = $_SERVER['HTTP_X_KARZOUN_SIGNATURE_256'] ?? '';
  $body = file_get_contents('php://input');

  if (!str_starts_with($header, 'sha256=')) {
    http_response_code(401);
    exit;
  }

  $expected = substr($header, 7);
  $computed = hash_hmac('sha256', $body, $secret);

  if (!hash_equals($expected, $computed)) {
    http_response_code(401);
    exit;
  }

  http_response_code(200);
  ```
</CodeGroup>

### التحقق من الرمز

قارن ترويسة `Karzoun-token` بـ `token` المُعاد عند إنشاء الـ webhook. هذا فحص سر مشترك بسيط — اقرنه بالتحقق عبر HMAC للدفاع المتعمق.

## تسجيل webhook

سجّل الاشتراكات عبر `webhooksAdd`. تنشئ كرزون `token` فريداً و`secret` لـ HMAC، وتتحقق من عنوانك (HTTPS + فحوصات SSRF)، وترسل **اختبار اتصال** تلقائياً.

| المعامل                   | النوع  | الوصف                                                                    |
| ------------------------- | ------ | ------------------------------------------------------------------------ |
| `url`                     | string | **مطلوب.** نقطة نهاية HTTPS تستقبل طلبات POST                            |
| `name`                    | string | تسمية مقروءة للبشر                                                       |
| `description`             | string | الغرض من هذا الـ webhook                                                 |
| `actions`                 | array  | اشتراكات الإجراءات — كل عنصر يحتوي `type` و`action` و`label` اختيارياً   |
| `retryPolicy.maxAttempts` | int    | الحد الأقصى لمحاولات التسليم (الافتراضي: **3**)                          |
| `retryPolicy.backoffMs`   | int\[] | التأخير بين المحاولات بالميلي ثانية (الافتراضي: **5000، 30000، 120000**) |
| `rateLimitPerMinute`      | int    | الحد الأقصى للتسليمات في الدقيقة لكل webhook (الافتراضي: **100**)        |

<Info>
  استعلم `webhooksGetActions` لسرد كل حدث يمكن لمساحة عملك الاشتراك فيه. الكتالوج أدناه يعكس وحدات المنصة الحالية.
</Info>

<CodeGroup>
  ```graphql GraphQL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  mutation {
    webhooksAdd(
      url: "https://api.example.com/karzoun/events"
      name: "CRM + inbox sync"
      description: "Customer and conversation updates"
      actions: [
        { type: "core:customer", action: "create" }
        { type: "core:customer", action: "update" }
        { type: "inbox:conversation", action: "create" }
        { type: "inbox:conversation", action: "assign" }
        { type: "whatsapp:flow", action: "completed" }
      ]
      retryPolicy: { maxAttempts: 3, backoffMs: [5000, 30000, 120000] }
    ) {
      _id
      url
      token
      secret
      actions {
        type
        action
        label
      }
      retryPolicy {
        maxAttempts
        backoffMs
      }
    }
  }
  ```

  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST 'https://YOUR_SUBDOMAIN.api.karzoun.chat/graphql' \
    -H 'Content-Type: application/json' \
    -H 'x-app-token: YOUR_APP_TOKEN' \
    -d '{
      "query": "mutation { webhooksAdd(url: \"https://api.example.com/karzoun/events\", name: \"My hook\", actions: [{ type: \"core:customer\", action: \"update\" }]) { _id token secret } }"
    }'
  ```
</CodeGroup>

<Tip>
  جرّب هذا في [ساحة GraphQL](https://karzoun.chat/developer/playground).
</Tip>

**احفظ بيانات الاعتماد فوراً.** يُعاد `token` و`secret` عند الإنشاء. استخدم `webhooksRotateSecret` لتدوير سر HMAC؛ يبقى السر السابق صالحاً لمدة 24 ساعة.

### عمليات الإدارة الأخرى

| الطفرة                  | الوصف                                                               |
| ----------------------- | ------------------------------------------------------------------- |
| `webhooksEdit`          | تحديث العنوان، الاسم، الإجراءات، سياسة إعادة المحاولة، أو حد المعدل |
| `webhooksRemove`        | حذف webhook وسجلات تسليمه                                           |
| `webhooksToggle`        | إيقاف أو استئناف التسليمات دون حذف الإعداد                          |
| `webhooksRotateSecret`  | إنشاء سر HMAC جديد (سماح 24 ساعة للقديم)                            |
| `webhooksPing`          | إرسال حدث اختبار `system.ping` للتحقق من الاتصال                    |
| `webhooksRetryDelivery` | إعادة محاولة يدوية لسجل تسليم فاشل                                  |
| `webhooksReset`         | إعادة تعيين قاطع الدائرة وعدادات الأخطاء بعد إصلاح نقطة النهاية     |
| `webhookDeliveryLogs`   | فحص تفاصيل الطلب/الاستجابة للتصحيح                                  |

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

كل تسليم يستخدم نفس شكل الغلاف (`version` حالياً **`1`**):

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "version": "1",
  "timestamp": "2026-06-23T14:30:00.000Z",
  "event": "inbox:conversation.create",
  "data": {
    "_id": "abc123",
    "status": "open",
    "customerId": "cust_456"
  },
  "metadata": {
    "webhookId": "wh_789",
    "deliveryId": "del_012",
    "attemptNumber": 1,
    "description": "Customer has started a new conversation",
    "resourceUrl": "https://YOUR_SUBDOMAIN.karzoun.chat/inbox?conversation=abc123"
  }
}
```

| الحقل                    | الوصف                                                              |
| ------------------------ | ------------------------------------------------------------------ |
| `id`                     | معرّف حدث فريد — استخدمه كـ **مفتاح تكرار** لإزالة تكرار المحاولات |
| `event`                  | الاسم الكامل: `<type>.<action>` (مثلاً `core:customer.update`)     |
| `data`                   | كائن JSON أصلي للمورد المتأثر — لا يُضاعَف تسلسله كنص أبداً        |
| `metadata.attemptNumber` | رقم محاولة التسليم هذه (1 = المحاولة الأولى)                       |
| `metadata.resourceUrl`   | رابط عميق للمورد في واجهة كرزون                                    |

لأحداث `delete`، يُطبَّع `data` إلى `{ "type": "<type>", "object": { "_id": "..." } }`.

تستخدم اختبارات الاتصال `event: "system.ping"` مع رسالة اختبار قصيرة في `data`.

## إعادة المحاولة، والمهلات، وقاطع الدائرة

| الإعداد                   | الافتراضي | السلوك                                                                                        |
| ------------------------- | --------- | --------------------------------------------------------------------------------------------- |
| **مهلة HTTP**             | 10 ثوانٍ  | تنتظر كرزون حتى 10 ثوانٍ لاستجابة خادمك                                                       |
| **الحد الأقصى للمحاولات** | 3         | تُعاد محاولات التسليم الفاشل مع تأخير: 5ث → 30ث → 2د                                          |
| **قاطع الدائرة**          | 5 إخفاقات | بعد 5 إخفاقات متتالية، تتوقف التسليمات حتى انتهاء التبريد (5 دقائق) أو تستدعي `webhooksReset` |
| **حد المعدل**             | 100/دقيقة | سقف لكل webhook؛ الأحداث الزائدة تُسجَّل كمُتخطّاة                                            |

<Warning>
  أعد `2xx` فور قبول الحمولة. انقل العمل الثقيل إلى طابور خلفية — المعالجات البطيئة تعرضك للمهلات وإعادة المحاولة.
</Warning>

<Tip>
  قد يصل نفس الحدث المنطقي أكثر من مرة (`id` يبقى ثابتاً عبر المحاولات؛ `metadata.deliveryId` يتغير لكل محاولة). صمّم المعالجات لتتحمّل التكرار.
</Tip>

## كتالوج الأحداث

تُعرَّف الأحداث بـ **`type`** (الوحدة + المورد) و\*\*`action`\*\* (ما حدث). في الحمولة المُسلَّمة تظهر كـ `event: "<type>.<action>"`.

استعلم `webhooksGetActions` للقائمة الحية. الاشتراكات أدناه مجمّعة حسب وحدة المنصة.

### العملاء (`core:customer`)

| الحدث                  | `action` | الوصف             |
| ---------------------- | -------- | ----------------- |
| `core:customer.create` | `create` | تم إنشاء سجل عميل |
| `core:customer.update` | `update` | تم تحديث سجل عميل |
| `core:customer.delete` | `delete` | تم حذف سجل عميل   |
| `core:customer.merge`  | `merge`  | تم دمج سجلَي عميل |

### المحادثات والبريد الوارد (`inbox:*`)

| الحدث                         | `action`   | الوصف                                       |
| ----------------------------- | ---------- | ------------------------------------------- |
| `inbox:conversation.create`   | `create`   | بدأت محادثة جديدة                           |
| `inbox:conversation.status`   | `status`   | تغيّرت حالة المحادثة (مثلاً مفتوحة → مغلقة) |
| `inbox:conversation.assign`   | `assign`   | عُيّنت المحادثة لوكيل                       |
| `inbox:conversation.unassign` | `unassign` | أُلغِي تعيين المحادثة                       |
| `inbox:conversation.update`   | `update`   | حُدّثت بيانات تعريف المحادثة                |
| `inbox:feedback.create`       | `create`   | قدّم العميل تقييماً                         |
| `inbox:summary.create`        | `create`   | أُنشئ ملخص محادثة بالذكاء الاصطناعي         |
| `inbox:popupSubmitted.create` | `create`   | قدّم العميل نموذجاً منبثقاً                 |

### واتساب (`whatsapp:*`)

| الحدث                     | `action`    | الوصف                       |
| ------------------------- | ----------- | --------------------------- |
| `whatsapp:order.received` | `received`  | استُلم طلب عبر تجارة واتساب |
| `whatsapp:flow.completed` | `completed` | أكمل العميل تدفق واتساب     |

### المهام (`tasks:*`)

| الحدث                          | `action`          | الوصف                         |
| ------------------------------ | ----------------- | ----------------------------- |
| `tasks:task.create`            | `create`          | تم إنشاء مهمة                 |
| `tasks:task.update`            | `update`          | تم تحديث مهمة                 |
| `tasks:task.delete`            | `delete`          | تم حذف مهمة                   |
| `tasks:task.assign`            | `assign`          | تم تعيين مهمة                 |
| `tasks:task.stage_change`      | `stage_change`    | نُقلت المهمة إلى مرحلة مختلفة |
| `tasks:task.priority_change`   | `priority_change` | تغيّرت أولوية المهمة          |
| `tasks:task.due_date`          | `due_date`        | تغيّر تاريخ استحقاق المهمة    |
| `tasks:checklist.item_checked` | `item_checked`    | عُلّمت عنصر قائمة تحقق كمكتمل |

### الاجتماعات (`meetings:meeting`)

| الحدث                     | `action` | الوصف                  |
| ------------------------- | -------- | ---------------------- |
| `meetings:meeting.create` | `create` | جُدول اجتماع           |
| `meetings:meeting.update` | `update` | تغيّرت تفاصيل الاجتماع |
| `meetings:meeting.cancel` | `cancel` | أُلغِي الاجتماع        |
| `meetings:meeting.start`  | `start`  | بدأ الاجتماع           |

### ساعة الدوام (`timeclock:*`)

| الحدث                          | `action`    | الوصف                 |
| ------------------------------ | ----------- | --------------------- |
| `timeclock:shift.clock_in`     | `clock_in`  | سجّل الموظف حضوره     |
| `timeclock:shift.clock_out`    | `clock_out` | سجّل الموظف انصرافه   |
| `timeclock:absence.requested`  | `requested` | طُلب غياب / إجازة     |
| `timeclock:absence.approved`   | `approved`  | وُوفق على طلب الغياب  |
| `timeclock:absence.rejected`   | `rejected`  | رُفض طلب الغياب       |
| `timeclock:schedule.submitted` | `submitted` | قُدّم الجدول للموافقة |
| `timeclock:schedule.approved`  | `approved`  | وُوفق على الجدول      |
| `timeclock:schedule.rejected`  | `rejected`  | رُفض الجدول           |

### النظام

| الحدث         | `action` | الوصف                                                          |
| ------------- | -------- | -------------------------------------------------------------- |
| `system.ping` | `ping`   | اختبار اتصال يُرسل عبر `webhooksPing` أو عند إنشاء الـ webhook |

## استكشاف الأخطاء

### تأكد أن كرزون ترسل الأحداث

* افتح **المطوّر ← Webhooks ← سجلات التسليم** للاشتراك
* نفّذ `webhooksPing` وتحقق من تسليم `system.ping` بحالة `success`
* وجّه العنوان مؤقتاً إلى [webhook.site](https://webhook.site) لفحص الطلبات الخام

### افحص نقطة نهايتك

| العرض                | السبب المحتمل                                                     |
| -------------------- | ----------------------------------------------------------------- |
| لا طلبات على الإطلاق | webhook متوقف (`isActive: false`)، قاطع مفتوح، أو اشتراك حدث خاطئ |
| `401` من جانبك       | فشل التحقق من الرمز/التوقيع — قارن الجسم الخام وليس JSON المحلّل  |
| مهلات                | المعالج بطيء جداً؛ توقف كرزون بعد **10 ثوانٍ**                    |
| حالة `circuit-open`  | خمسة إخفاقات متتالية — أصلح نقطة النهاية ثم `webhooksReset`       |
| تسليمات متخطّاة      | تجاوز حد المعدل لذلك الـ webhook                                  |

### متطلبات التحقق من العنوان

* يجب أن يكون **HTTPS**
* يجب أن يُحل إلى عنوان IP **عام** (النطاقات الخاصة وlocalhost محظورة لحماية SSRF)
* يجب أن يعيد **2xx** حتى تعتبر كرزون التسليم ناجحاً

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

* [مرجع GraphQL API](/api-reference) — ابحث عن `Webhook` و`WebhookDeliveryLog`
* [المصادقة](/ar/developers/getting-started/authentication) — رموز التطبيقات للوصول إلى GraphQL
* [الأخطاء](/ar/developers/guides/errors) — تفسير استجابات أخطاء GraphQL وHTTP

<Tip>
  اسرد webhooks في [الساحة](https://karzoun.chat/developer/playground): `query { webhooks(page: 1, perPage: 5) { data { _id name url status isActive } } }`
</Tip>


## Related topics

- [التجارة الإلكترونية](/ar/miniapps/guides/ecommerce.md)
- [أنماط الوكلاء](/ar/mcp-server/guides/agent-patterns.md)
- [مرجع واجهة البرمجة](/ar/miniapps/reference/api-reference.md)
- [الويب هوك](/ar/miniapps/guides/webhooks.md)
- [المزامنة](/ar/miniapps/guides/sync.md)
