---
title: "نظرة عامة على Webhooks"
description: "كيفية تسليم ThunderPhone للأحداث في الوقت الفعلي، وكيفية التحقق من التوقيعات، ومقارنة نموذجَي التسليم القديم والقائم على نقاط النهاية."
---

يرسل ThunderPhone طلبات HTTP من نوع `POST` إلى خادمك عند وقوع أحداث أثناء المكالمة، مثل بدء مكالمة واردة أو انتهائها أو اكتمال تشغيل تقييم أو إطلاق تنبيه، وغير ذلك. هناك **نموذجان للتسليم**:

<CardGroup cols={2}>
  <Card title="نقاط نهاية Webhook (موصى بها)" icon="bolt" href="/ar/webhooks/endpoints">
    عناوين URL متعددة، وأسرار لكل نقطة نهاية، وفلاتر أحداث لكل نقطة نهاية،
    وإعادات محاولة تلقائية.
    تُدار عبر `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Webhook قديم بعنوان URL واحد" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    عنوان URL واحد لكل مؤسسة. يحمل أحداث دورة حياة المكالمة، بما فيها
    تبادلات الإعدادات **الحاجبة**. يُدار عبر `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

تُسلَّم جميع أنواع الأحداث المدرجة في [كتالوج الأحداث](/ar/webhooks/events) عبر نقاط نهاية Webhook. كما تُرسل أحداث دورة حياة المكالمة الستة
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) **أيضًا** إلى Webhook القديم ذي عنوان URL الواحد؛ فإذا كان لديك عنوان URL قديم ونقطة نهاية مطابقة، فستتلقى الحدث عبر **كلا** المسارين. يقتصر السلوك الحاجب (تبادل إعدادات [`telephony.incoming` / `web.incoming`](/ar/webhooks/call-incoming) و[إرسال الأدوات](/ar/tools/overview) في وضع Webhook) على المسار القديم؛ وكل تسليم إلى نقطة نهاية هو إشعار يُرسل دون انتظار.

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

تكون عمليات التسليم إلى نقاط النهاية كائن JSON يحتوي على `data` و`event_id` و`type`:

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

يكون `event_id` فريدًا لكل حدث يتم إصداره. ويظل مطابقًا عبر إعادات المحاولة
**وعبر** كل نقطة نهاية تتلقى الحدث، لذا أزل التكرارات بالاعتماد عليه.

يرسل Webhook القديم ذو عنوان URL الواحد القيمتين `type` و`data` نفسيهما، ولكن
**من دون** `event_id`:

```json
{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}
```

عند الإرسال، تُسلسَل كل حمولة بصيغة معيارية: تُرتَّب المفاتيح أبجديًا، ومن دون مسافات بيضاء، وبترميز UTF-8. الأمثلة المنسقة في هذه الوثائق مخصصة لسهولة القراءة فقط.

راجع [كتالوج الأحداث](/ar/webhooks/events) للاطلاع على القائمة الكاملة لأنواع الأحداث وحقول الحمولة.

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

يحمل كل طلب توقيعين، وكلاهما يستخدم HMAC-SHA256 بمفتاح هو `secret` الخاص
بنقطة النهاية (أو `secret` الخاص بخطاف الويب على مستوى مؤسستك لعمليات
التسليم القديمة):

- يوقّع `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` الطابع
  الزمني والطريقة والمسار والاستعلام والنص الخام. تحقّق من هذا التوقيع،
  وارفض الطلبات التي يزيد فيها `t` على **5 دقائق** مقارنةً بساعتك، حتى لا
  يمكن إعادة تشغيل طلب تم اعتراضه لاحقًا. يُعاد توقيع عمليات إعادة المحاولة
  عند إرسالها.
- يوقّع `X-ThunderPhone-Signature` النص الخام فقط. وما زال يُرسل دون تغيير،
  لكي تستمر أدوات التحقّق الحالية في العمل.

### الخطوات

1. اقرأ الرأس، والطريقة، والمسار والاستعلام الخامَّين، والنص
   الخام **قبل** أي تحليل.
2. احسب قيمة HMAC-SHA256 السداسية عشرية لـ
   `<t>\n<METHOD>\n<path and query>\n<body>` باستخدام سرّك.
3. ارفض الطلب إذا كان `t` متجاوزًا بفارق يزيد على 300 ثانية، أو إذا كانت
   النتيجة لا تطابق `v1` في مقارنة بزمن ثابت.

النصوص هي تسلسل JSON الأساسي (مفاتيح مرتبة وفواصل مضغوطة)، ونوقّع البايتات التي نرسلها بالضبط. يحتوي
[دليل التحقق](/ar/guides/verify-webhook-signatures) على السلسلة الموقعة الدقيقة،
ومثال عملي، وشيفرة من دون SDK، والرأس القديم.

<CodeGroup>
```python Python (Flask)
from flask import Flask, abort, request
from thunderphone import verify_signature

app = Flask(__name__)

@app.post("/thunderphone-webhook")
def handle():
    query = request.query_string.decode()
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=request.path + (f"?{query}" if query else ""),
        body=request.get_data(),  # raw bytes
    ):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
```

```javascript Node.js (Express)
import express from "express";
import { verifySignature } from "@thunderphone/sdk";

const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySignature({
      header: req.header("X-ThunderPhone-Signature-V2"),
      secret: process.env.WEBHOOK_SECRET,
      method: req.method,
      path: req.originalUrl,
      body: req.body,
    });
    if (!ok) return res.sendStatus(401);
    const event = JSON.parse(req.body.toString("utf8"));
    // dispatch on event.type …
    res.sendStatus(204);
  },
);
```
</CodeGroup>

## دلالات التسليم

تنطبق هذه الدلالات على عمليات تسليم **نقطة النهاية**. إن webhook القديم ذي عنوان URL واحد
هو محاولة متزامنة واحدة بلا إعادة محاولة، باستثناء تبادل إعدادات
مكالمة الهاتف [`telephony.incoming`](/ar/webhooks/call-incoming)
الذي يُعاد مرة واحدة.

<AccordionGroup>
  <Accordion title="إعادة المحاولات">
    تتم محاولة كل حدث مرة واحدة فورًا. أي استجابة `2xx`
    تُقِرّ بالتسليم. عند أي نتيجة أخرى (غير 2xx،
    خطأ في الاتصال، انتهاء المهلة)، نعيد المحاولة **بعد المحاولة الأولى بـ 1 دقيقة، و5 دقائق، و30 دقيقة، وساعتين، و6 ساعات،
    و12 ساعة، و24 ساعة** — 8 محاولات تمتد على مدار
    24 ساعة. إذا فشلت كل محاولة، يتوقف التسليم وتُعلَّم نقطة النهاية
    بالقيمة `status="failing"` في
    [نقاط نهاية webhook](/ar/webhooks/endpoints)؛ ونرسل بريدًا إلكترونيًا إلى
    المالكين والمشرفين في مؤسستك ونعرض ذلك في لوحة التحكم. أعد `2xx` بمجرد
    قبول الحمولة بشكل دائم؛ وعالجها بشكل غير متزامن.
    تحصل عمليات الإرسال الاختبارية من واجهة API لاختبار نقطة النهاية على إعادة محاولة واحدة بعد نحو دقيقة من
    المحاولة الأولى، ولا تغيّر حالة نقطة النهاية مطلقًا.
  </Accordion>

  <Accordion title="الترتيب">
    ترتيب التسليم هو بأفضل جهد. عمليًا، نُسلّم الأحداث بالترتيب الذي
    تُصدر به، لكن إعادة المحاولات قد تغيّر الترتيب عند الفشل.
    أزل التكرارات دائمًا وطابِق البيانات باستخدام `call_id` / معرّف الكائن.
  </Accordion>

  <Accordion title="التكرارات">
    التسليم **مرة واحدة على الأقل**: قد تؤدي إعادة المحاولة بعد استجابة لم
    نرها إلى تكرار حدث. تحمل كل إعادة محاولة المعرّف نفسه
    `event_id`، لذا خزّن المعرّفات التي عولجت وتخطَّ التكرارات. يُشارك `event_id`
    أيضًا بين نقاط النهاية — إذ تتلقى نقطتا نهاية مشتركتان في
    الحدث نفسه `event_id` نفسه.
  </Accordion>

  <Accordion title="انتهاء المهلات">
    تتضمن عمليات تسليم نقطة النهاية مهلة قدرها **30 ثانية** لكل محاولة. في
    المسار القديم، تقتصر الطلبات الحاجبة التي تتحكم في سلوك المكالمة الحية — أي
    تبادل الإعدادات [`telephony.incoming` / `web.incoming`](/ar/webhooks/call-incoming)
    — على **10 ثوانٍ** للمحاولة الأولى لمكالمة هاتفية و**13 ثانية** للتبادل
    كاملًا، بما في ذلك إعادة المحاولة الواحدة، وعلى **10 ثوانٍ** لجلسة أداة مصغّرة. تؤخر
    الاستجابة البطيئة الرد على المكالمة، لذا احرص على الاستجابة خلال ثانيتين تقريبًا. يتيح [إرسال الأدوات](/ar/tools/overview) في وضع webhook مدة 20 ثانية
    افتراضيًا، ويمكن لتعريفات الأدوات تعيين `timeout` على المستوى الأعلى.
  </Accordion>

  <Accordion title="عناوين IP المصدر">
    تنشأ عمليات webhook الصادرة من نطاق عناوين IP السحابية لـ ThunderPhone.
    إذا كان جدار الحماية لديك يتطلب قائمة سماح، فاتصل بالدعم وسنشاركك
    النطاقات الحالية.
  </Accordion>
</AccordionGroup>

## الاختيار بين عمليات webhook القديمة وتلك المستندة إلى نقاط النهاية

| الميزة | القديم (`/v1/webhook`) | نقاط النهاية (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| عدد عناوين URL | 1 لكل مؤسسة | عدة عناوين لكل مؤسسة |
| تغطية الأحداث | `telephony.*` / `web.*` فقط | جميع أنواع الأحداث العشرة |
| تصفية الأحداث | — | لكل نقطة نهاية |
| عمليات إعادة المحاولة | لا توجد، باستثناء واحدة لتبادل إعدادات المكالمة الهاتفية | 8 محاولات خلال 24 ساعة |
| الغلاف | `type` + `data` | `type` + `data` + `event_id` |
| تدوير السر | يستبدل السر المفرد | سر لكل نقطة نهاية |
| التعطيل دون حذف | `PUT /v1/webhook` مع `{"url": ""}` | `status=disabled` |
| إظهار الحالة | — | `active` / `disabled` / `failing` |
| تبادل الإعدادات الحاجب | نعم ([`telephony.incoming` / `web.incoming`](/ar/webhooks/call-incoming)) | مطلقًا — إشعارات فقط |
| الأنسب لـ | إعداد المكالمات الديناميكي | استهلاك الأحداث في بيئة الإنتاج |

ينبغي لعمليات التكامل الجديدة استهلاك الأحداث عبر عمليات webhook المستندة إلى
نقاط النهاية. احتفظ بعنوان URL قديم (أو أضفه) فقط إذا كنت تضبط المكالمات
بشكل ديناميكي عند وقت الرد أو تستخدم إرسال الأدوات في وضع webhook — فتبادلات
الطلب/الاستجابة هذه لا تعمل إلا عبر المسار القديم.

---

## ذو صلة

<CardGroup cols={2}>
  <Card title="كتالوج الأحداث" icon="list" href="/ar/webhooks/events">
    جميع أنواع الأحداث وحمولاتها.
  </Card>
  <Card title="نقاط نهاية Webhook" icon="bolt" href="/ar/webhooks/endpoints">
    أدر نقاط نهاية متعددة وفلاتر الأحداث والأسرار.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/ar/webhooks/call-incoming">
    الطلب المعطِّل الذي يجب أن يجيب عنه خادمك لإعداد المكالمات.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/ar/webhooks/call-complete">
    حمولة ما بعد المكالمة تتضمن النص المفرغ والتسجيل والمقاييس.
  </Card>
</CardGroup>
