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

# تهيئة ديناميكية لكل مكالمة

> اختر وكيلاً — أو أعد كتابة موجّه — لكل مكالمة واردة استنادًا إلى منطق مخصص في webhook.

افتراضيًا، يُعيَّن وكيل ثابت لكل رقم هاتف ومفتاح قابل للنشر. عندما تحتاج إلى تخصيص **لكل متصل** أو **لكل زائر** — مثل توجيه عملاء VIP، وسياق المستخدمين المسجّلين الدخول، واختبارات A/B للموجّهات — انتقل إلى وضع webhook ودع خادمك يقرر.

## آلية العمل

1. اشترك في حدث [`telephony.incoming`](/ar/webhooks/events)
   (الهاتف) أو [`web.incoming`](/ar/webhooks/events) (الودجت).
   كلاهما webhook **حاجب**: ينتظر ThunderPhone لمدة تصل إلى
   10 ثوانٍ ردّك قبل متابعة المكالمة.
2. يرسل إليك ThunderPhone `{call_id, from_number, to_number}` (تحمل جلسات الودجت
   حقولًا خاصة بالودجت بدلًا من الأرقام — راجع
   [مخطط الطلب](/ar/webhooks/call-incoming)).
3. يستجيب خادمك بتكوين وكيل (الموجّه، الصوت،
   المنتج، الأدوات). يستخدم ThunderPhone هذا التكوين للمكالمة.
4. إذا أرجعت `{}`، أو انتهت المهلة، أو حدث خطأ، يُستخدم
   الوكيل المعيّن ثابتًا كبديل. إعداد افتراضي آمن.

<Note>
  يعمل بالطريقة نفسها للمكالمات الهاتفية (`telephony.incoming`) وجلسات
  الودجت (`web.incoming`)، سواء جرى تسليمه إلى نقطة نهاية webhook
  أو إلى webhook القديم ذي عنوان URL الواحد.
</Note>

## 1. تكوين وجهة webhook

<Tabs>
  <Tab title="مكالمات هاتفية">
    بالنسبة إلى أرقام الهاتف، اشترك بنقطة النهاية الخاصة بك في `telephony.incoming`:

    ```bash theme={null}
    curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
      -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "label":  "Prod call-incoming",
        "url":    "https://example.com/thunderphone/incoming",
        "events": ["telephony.incoming"]
      }'
    ```

    يتضمن الرد `secret` للاستخدام لمرة واحدة — احفظه؛ ستستخدمه
    للتحقق من التوقيع.
  </Tab>

  <Tab title="ودجت الويب">
    بالنسبة إلى جلسات الودجت، أنشئ مفتاحًا قابلًا للنشر في `mode="webhook"`
    مع تضمين عنوان URL لنقطة النهاية الخاصة بك فيه:

    ```bash theme={null}
    curl -X POST https://api.thunderphone.com/v1/publishable-key \
      -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name":            "Dynamic widget",
        "mode":            "webhook",
        "webhook_url":     "https://example.com/thunderphone/widget-incoming",
        "allowed_domains": ["example.com"]
      }'
    ```

    سيُرسل الودجت طلب POST إلى عنوان URL هذا عند بدء كل جلسة.
  </Tab>
</Tabs>

## 2. نفّذ المعالج

ثلاث قواعد عملية:

* **تحقّق من التوقيع** في كل طلب (راجع
  [التحقق من توقيعات webhook](/ar/guides/verify-webhook-signatures)).
  لا تتجاوز هذه الخطوة في بيئة التطوير — نفّذها بشكل صحيح مرة واحدة وأعِد استخدامها.
* **استجب بسرعة**. عشر ثوانٍ هي الحد الأقصى الصارم، وكل ثانية هي
  صمت للمُتصل. أجرِ عمليات البحث في قاعدة البيانات عند الحاجة، لكن
  لا تستدعِ نماذج اللغة الكبيرة اللاحقة بشكل متزامن — إذا أردت إنشاء موجّه ديناميكيًا، فاحسبه مسبقًا وخزّنه مؤقتًا.
* **استخدم بديلًا بسلاسة**. يجب أن تعيد أي حالة غير متوقعة `{}` لكي
  يتولى وكيل الصوت المعيّن ثابتًا معالجة المكالمة.

<CodeGroup>
  ```python FastAPI theme={null}
  import hashlib
  import hmac
  import json
  import os

  from fastapi import FastAPI, HTTPException, Request

  app = FastAPI()
  SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]

  def verify(body: bytes, sig: str) -> bool:
      expected = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(expected, sig or "")

  @app.post("/thunderphone/incoming")
  async def incoming(request: Request):
      body = await request.body()
      if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
          raise HTTPException(401)

      event = json.loads(body)
      if event["type"] not in ("telephony.incoming", "web.incoming"):
          return {}  # fall back to default

      caller = event["data"]["from_number"]
      # Cheap DB lookup: is this a known VIP?
      customer = lookup_customer(caller)
      if customer and customer.tier == "vip":
          return {
              "prompt":  f"You are a VIP concierge for {customer.name}. Be proactive…",
              "voice":   "john",
              "product": "storm-base",
          }
      return {}  # default agent handles non-VIPs

  def lookup_customer(phone: str):
      # ... your CRM integration ...
      pass
  ```

  ```javascript Express theme={null}
  import crypto from "node:crypto";
  import express from "express";

  const app = express();
  const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;

  function verify(body, sig) {
    const expected = crypto.createHmac("sha256", SECRET).update(body).digest("hex");
    return sig &&
      crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
  }

  app.post(
    "/thunderphone/incoming",
    express.raw({ type: "application/json" }),
    async (req, res) => {
      if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
        return res.sendStatus(401);
      }
      const event = JSON.parse(req.body.toString("utf8"));

      const IMPORTANT_TYPES = new Set([
        "telephony.incoming",
        "web.incoming",
      ]);
      if (!IMPORTANT_TYPES.has(event.type)) return res.json({});

      const customer = await lookupCustomer(event.data.from_number);
      if (customer?.tier === "vip") {
        return res.json({
          prompt:  `You are a VIP concierge for ${customer.name}. Be proactive…`,
          voice:   "john",
          product: "storm-base",
        });
      }
      res.json({}); // fall back to default agent
    },
  );
  ```
</CodeGroup>

## 3. مخطط الاستجابة

يطابق نص الاستجابة
[مخطط استجابة المكالمات الواردة](/ar/webhooks/call-incoming)
تمامًا. الحقول الشائعة الاستخدام:

| الحقل                         | النوع              | الوصف                                                                 |
| ----------------------------- | ------------------ | --------------------------------------------------------------------- |
| `prompt`                      | سلسلة نصية (مطلوب) | الموجّه النظامي للوكيل                                                |
| `voice`                       | سلسلة نصية (مطلوب) | معرّف الصوت من [`GET /v1/voices`](/api-reference/agents#voices)       |
| `product`                     | سلسلة نصية         | القيمة الافتراضية هي `spark`                                          |
| `background_track`            | سلسلة نصية \| null | معرّف الصوت المحيط                                                    |
| `acknowledgement_prompt_mode` | سلسلة نصية         | `auto` أو `manual` (Storm مع الإقرار فقط)                             |
| `acknowledgement_prompt`      | سلسلة نصية         | مطلوب عندما يكون الوضع `manual`                                       |
| `tools`                       | مصفوفة             | مخططات أدوات الدوال المضمنة — راجع [أدوات الدوال](/ar/tools/overview) |

<Note>
  لا تتوفر إعدادات ترتيب التحدث لكل مكالمة و`max_hold_seconds` في
  استجابة webhook. اضبطها في
  [الوكيل](/api-reference/agents) الذي تشير إليه.
</Note>

## الأنماط

### سياق المستخدم المسجّل الدخول

في عناصر الواجهة المصغّرة بوضع webhook، تعرف صفحة الزائر مسبقًا من
يكون. استدعِ webhook الخاص بك باستخدام مَعلمة سلسلة استعلام يمرّرها
SDK عنصر الواجهة (`?customer_id=123`) وابحث عن العميل من جهة الخادم.

### طرح موجّهات A/B تدريجيًا

قبل تنفيذ ذلك يدويًا، لاحظ أن ThunderPhone يوفر ميزة
[التجارب](/ar/guides/concepts) أصلية
(`/dashboard/experiments` وعلامة التبويب **A/B** في منشئ الوكيل) التي
تعرّف المتغيرات، وتقسم الزيارات، وتقارن النتائج لكل متغير —
دون الحاجة إلى webhook.

إذا كنت تحتاج إلى التحكم من جهة webhook على أي حال: حوّل `call_id` إلى قيمة تجزئة → مجموعة؛
قدّم الموجّه A للقيم `0..49` والموجّه B للقيم `50..99`. سجّل المجموعة التي
اخترتها في قاعدة البيانات الخاصة بك، واربطها لاحقًا بتقييم المكالمة المكتملة.

### توجيه قائم على الوقت

ساعات العمل → وكيل "الدعم المباشر"؛ خارج ساعات العمل → وكيل "تسجيل رسالة".
تبديل بسيط يعتمد على `new Date().getUTCHours()` في معالجك.

***

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

<CardGroup cols={2}>
  <Card title="مرجع webhook للمكالمات الواردة" icon="phone" href="/ar/webhooks/call-incoming">
    مخططات الطلب والاستجابة الدقيقة، بما في ذلك كل مفتاح إعداد.
  </Card>

  <Card title="التحقق من توقيعات webhook" icon="shield-check" href="/ar/guides/verify-webhook-signatures">
    اضبط HMAC بشكل صحيح مرة واحدة؛ وأعد استخدامه في كل مكان.
  </Card>

  <Card title="إنشاء تكامل أداة" icon="screwdriver-wrench" href="/ar/guides/build-tool-integration">
    اجمع بين التوجيه الديناميكي والأدوات الخاصة بكل وكيل.
  </Card>

  <Card title="دلالات التسليم" icon="bolt" href="/ar/webhooks/overview">
    إعادة المحاولة، والترتيب، والمهلات.
  </Card>
</CardGroup>
