> ## 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.

# डायनेमिक प्रति-कॉल कॉन्फ़िगरेशन

> वेबहुक में कस्टम लॉजिक के आधार पर हर इनकमिंग कॉल के लिए एक एजेंट चुनें — या प्रॉम्प्ट को फिर से लिखें।

डिफ़ॉल्ट रूप से हर फ़ोन नंबर और publishable key को एक स्थिर एजेंट
असाइन किया जाता है। जब आपको **हर कॉलर के लिए** या **हर विज़िटर के लिए**
कस्टमाइज़ेशन चाहिए — VIP रूटिंग, लॉग-इन किए हुए यूज़र का कॉन्टेक्स्ट, A/B प्रॉम्प्ट टेस्ट — तो
webhook-mode पर स्विच करें और अपने सर्वर को निर्णय लेने दें।

## यह कैसे काम करता है

1. आप [`telephony.incoming`](/hi/webhooks/events)
   (फ़ोन) या [`web.incoming`](/hi/webhooks/events) (विजेट)
   इवेंट को सब्सक्राइब करते हैं। दोनों **ब्लॉकिंग** वेबहुक हैं: ThunderPhone
   कॉल जारी रखने से पहले आपके रिस्पॉन्स के लिए 10 सेकंड तक इंतज़ार करता है।
2. ThunderPhone आपको `{call_id, from_number, to_number}` भेजता है (विजेट
   सेशन में नंबरों के बजाय विजेट-विशिष्ट फ़ील्ड होते हैं — [रिक्वेस्ट स्कीमा](/hi/webhooks/call-incoming)
   देखें)।
3. आपका सर्वर एजेंट कॉन्फ़िगरेशन (प्रॉम्प्ट, वॉइस,
   प्रोडक्ट, टूल्स) के साथ रिस्पॉन्ड करता है। ThunderPhone उस कॉन्फ़िगरेशन का इस्तेमाल कॉल के लिए करता है।
4. अगर आप `{}` रिटर्न करते हैं, टाइम आउट होते हैं या एरर होता है, तो स्थिर रूप से असाइन किया गया
   एजेंट फ़ॉलबैक के तौर पर इस्तेमाल होता है। सुरक्षित डिफ़ॉल्ट।

<Note>
  फ़ोन कॉल (`telephony.incoming`) और विजेट
  सेशन (`web.incoming`) के लिए यह एक समान रूप से काम करता है, चाहे इसे वेबहुक एंडपॉइंट
  पर या लेगेसी सिंगल-URL वेबहुक पर डिलीवर किया जाए।
</Note>

## 1. वेबहुक डेस्टिनेशन कॉन्फ़िगर करें

<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"]
      }'
    ```

    रिस्पॉन्स में एक one-shot `secret` शामिल होता है — इसे सेव करें; आप इसका उपयोग
    सिग्नेचर वेरिफ़िकेशन के लिए करेंगे।
  </Tab>

  <Tab title="वेब विजेट">
    विजेट सेशन के लिए, अपने एंडपॉइंट URL के साथ `mode="webhook"` में एक publishable key बनाएँ:

    ```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"]
      }'
    ```

    हर सेशन शुरू होने पर विजेट इस URL पर POST करेगा।
  </Tab>
</Tabs>

## 2. हैंडलर इम्प्लीमेंट करें

तीन व्यावहारिक नियम:

* हर रिक्वेस्ट पर **सिग्नेचर वेरिफ़ाई करें** (देखें
  [वेबहुक सिग्नेचर वेरिफ़ाई करें](/hi/guides/verify-webhook-signatures))।
  इसे dev में स्किप न करें — एक बार सही करें और दोबारा इस्तेमाल करें।
* **तेज़ी से रिस्पॉन्ड करें**। दस सेकंड की हार्ड लिमिट है, और हर सेकंड
  कॉलर के लिए खामोशी है। ज़रूरत हो तो डेटाबेस लुकअप करें, लेकिन
  डाउनस्ट्रीम LLMs को सिंक्रोनस रूप से कॉल न करें — डायनेमिक प्रॉम्प्ट जनरेशन चाहिए,
  तो पहले से कंप्यूट करें और कैश करें।
* **साफ़ तरीके से फॉल बैक करें**। किसी भी अप्रत्याशित स्थिति में `{}` रिटर्न होना चाहिए,
  ताकि स्टैटिक रूप से असाइन किया गया एजेंट कॉल संभाल सके।

<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. रिस्पॉन्स स्कीमा

रिस्पॉन्स बॉडी
[इनकमिंग-कॉल रिस्पॉन्स स्कीमा](/hi/webhooks/call-incoming)
से बिल्कुल मेल खाती है। आम तौर पर इस्तेमाल होने वाले फ़ील्ड:

| फ़ील्ड                        | टाइप            | विवरण                                                                 |
| ----------------------------- | --------------- | --------------------------------------------------------------------- |
| `prompt`                      | string (आवश्यक) | एजेंट के लिए सिस्टम प्रॉम्प्ट                                         |
| `voice`                       | string (आवश्यक) | [`GET /v1/voices`](/api-reference/agents#voices) से वॉइस id           |
| `product`                     | string          | डिफ़ॉल्ट रूप से `spark`                                               |
| `background_track`            | string \| null  | एम्बिएंट ऑडियो id                                                     |
| `acknowledgement_prompt_mode` | string          | `auto` या `manual` (केवल Storm-with-ack)                              |
| `acknowledgement_prompt`      | string          | मोड `manual` होने पर आवश्यक                                           |
| `tools`                       | array           | इनलाइन फ़ंक्शन-टूल स्कीमा — देखें [फ़ंक्शन टूल्स](/hi/tools/overview) |

<Note>
  प्रति-कॉल स्पीक-ऑर्डर और `max_hold_seconds` वेबहुक रिस्पॉन्स में उपलब्ध नहीं हैं।
  इन्हें उस [एजेंट](/api-reference/agents) पर सेट करें
  जिसका आप रेफ़रेंस करते हैं।
</Note>

## पैटर्न

### लॉग-इन उपयोगकर्ता संदर्भ

वेबहुक-मोड विजेट्स में, विज़िटर के पेज को पहले से पता होता है कि वे कौन हैं।
अपने वेबहुक को एक क्वेरी स्ट्रिंग पैरामीटर के साथ कॉल करें, जिसे विजेट SDK
फॉरवर्ड करता है (`?customer_id=123`), और सर्वर-साइड पर ग्राहक को लुक अप करें।

### A/B प्रॉम्प्ट रोलआउट

इसे खुद बनाने से पहले, ध्यान दें कि ThunderPhone में एक नेटिव
[एक्सपेरिमेंट्स](/hi/guides/concepts) फीचर
(`/dashboard/experiments` और एजेंट बिल्डर का **A/B** टैब) है, जो
वेरिएंट्स परिभाषित करता है, ट्रैफ़िक विभाजित करता है, और प्रति वेरिएंट परिणामों की तुलना करता है —
किसी वेबहुक की आवश्यकता नहीं।

अगर फिर भी आपको वेबहुक-साइड नियंत्रण चाहिए: `call_id` को हैश करें → बकेट;
`0..49` के लिए प्रॉम्प्ट A और `50..99` के लिए प्रॉम्प्ट B सर्व करें। चुने गए
बकेट को अपने DB में रिकॉर्ड करें और बाद में उसे पूर्ण कॉल के ग्रेड से कोरिलेट करें।

### समय-आधारित रूटिंग

व्यावसायिक समय → "लाइव सपोर्ट" एजेंट; काम के घंटों के बाद → "संदेश लें"
एजेंट। अपने हैंडलर में `new Date().getUTCHours()` पर आधारित साधारण स्विच।

***

## अगले चरण

<CardGroup cols={2}>
  <Card title="इनकमिंग-कॉल वेबहुक रेफरेंस" icon="phone" href="/hi/webhooks/call-incoming">
    हर कॉन्फ़िगरेशन कुंजी सहित सटीक रिक्वेस्ट + रिस्पॉन्स स्कीमा।
  </Card>

  <Card title="वेबहुक सिग्नेचर सत्यापित करें" icon="shield-check" href="/hi/guides/verify-webhook-signatures">
    HMAC को एक बार सही करें; हर जगह पुनः उपयोग करें।
  </Card>

  <Card title="टूल इंटीग्रेशन बनाएं" icon="screwdriver-wrench" href="/hi/guides/build-tool-integration">
    डायनेमिक रूटिंग को प्रति-एजेंट टूल्स के साथ संयोजित करें।
  </Card>

  <Card title="डिलीवरी सेमांटिक्स" icon="bolt" href="/hi/webhooks/overview">
    रिट्राइज़, क्रम, टाइमआउट्स।
  </Card>
</CardGroup>
