---
title: "प्रत्येक कॉलसाठी डायनॅमिक कॉन्फिगरेशन"
description: "तुमच्या नियंत्रणातील webhook मधील सानुकूल लॉजिकद्वारे, प्रत्येक येणाऱ्या कॉलसाठी स्वतंत्रपणे उत्तर देणारा एजंट निवडा — किंवा त्याचा prompt आणि सेटिंग्ज पुन्हा लिहा."
---

डीफॉल्टनुसार प्रत्येक फोन नंबर आणि प्रकाशित करण्यायोग्य कीला एक स्थिर एजंट
नियुक्त केलेला असतो. तुम्हाला **प्रत्येक कॉलरसाठी** किंवा **प्रत्येक व्हिजिटरसाठी** सानुकूलन
हवे असल्यास — VIP राउटिंग, लॉग-इन केलेल्या वापरकर्त्याचा संदर्भ, A/B prompt चाचण्या — 
webhook मोडवर स्विच करा आणि तुमच्या सर्व्हरला निर्णय घेऊ द्या.

## हे कसे कार्य करते

1. तुम्ही [`telephony.incoming`](/mr/webhooks/events)
   (फोन) किंवा [`web.incoming`](/mr/webhooks/events) (विजेट)
   इव्हेंटचे सदस्यत्व घेता. दोन्ही **ब्लॉकिंग** webhook आहेत: ThunderPhone पुढे
   कॉल सुरू ठेवण्यापूर्वी तुमच्या प्रतिसादासाठी 10 सेकंदांपर्यंत थांबते.
2. ThunderPhone तुम्हाला `{call_id, from_number, to_number}` पाठवते (विजेट
   सेशनमध्ये नंबरऐवजी विजेट-विशिष्ट फील्ड असतात — [विनंती स्कीमा](/mr/webhooks/call-incoming)
   पहा).
3. तुमचा सर्व्हर एजंट कॉन्फिगरेशनसह प्रतिसाद देतो (prompt, व्हॉइस,
   प्रॉडक्ट, टूल्स). ThunderPhone कॉलसाठी ते कॉन्फिगरेशन वापरते.
4. तुम्ही `{}` परत केल्यास, वेळ संपल्यास किंवा त्रुटी आल्यास, फॉलबॅक म्हणून
   स्थिरपणे नियुक्त केलेला एजंट वापरला जातो. सुरक्षित डीफॉल्ट.

<Note>
  फोन कॉल्ससाठी (`telephony.incoming`) आणि विजेट
  सेशनसाठी (`web.incoming`) हे एकसारखेच कार्य करते, मग ते webhook endpoint
  वर किंवा लेगसी single-URL webhook वर वितरित केलेले असो.
</Note>

<Warning>
  **इनलाइन webhook-कॉन्फिगर केलेल्या कॉलवर ThunderPhone ची संमती घोषणा
  प्ले होत नाही.** हा मार्ग एजंट-स्तरीय कॉल-प्रारंभ घोषणा वगळतो
  आणि ThunderPhone च्या संमती-घोषणा फ्रेमवर्कमधून (सेवा अटी, "रेकॉर्डिंग आणि संमती"
  विभाग) स्पष्टपणे वगळलेला आहे. या कॉल्सवरील सर्व रेकॉर्डिंग, मॉनिटरिंग,
  AI-सहभाग आणि कॉलर-ओळख सूचना व संमतीची जबाबदारी तुमच्या
  संस्थेची आहे. इनलाइन webhook मोड सक्षम करण्यापूर्वी त्या तुमच्या स्वतःच्या फ्लोमध्ये —
  उदाहरणार्थ prompt च्या सुरुवातीच्या स्क्रिप्टमध्ये —
  द्या. जतन केलेल्या `agent_id` चा संदर्भ असलेला webhook प्रतिसाद त्या
  एजंटचे सामान्य रेकॉर्डिंग आणि प्रकटीकरण धोरण वापरतो.
</Warning>

## 1. webhook गंतव्य कॉन्फिगर करा

<Tabs>
<Tab title="फोन कॉल्स">
फोन नंबरसाठी, तुमच्या endpoint ला `telephony.incoming` चे सदस्यत्व द्या:

```bash
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="वेब विजेट">
विजेट सेशनसाठी, तुमच्या endpoint URL सह `mode="webhook"` मधील प्रकाशित करण्यायोग्य की तयार करा:

```bash
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. हँडलर लागू करा

तीन मूलभूत नियम:

- प्रत्येक विनंतीवर **स्वाक्षरी सत्यापित करा** ([वेबहुक स्वाक्षऱ्या सत्यापित करा](/mr/guides/verify-webhook-signatures) पहा).
  डेव्हलपमेंटमध्ये हे वगळू नका — एकदा योग्यरीत्या करा आणि पुन्हा वापरा.
- **त्वरित प्रतिसाद द्या**. दहा सेकंद ही कमाल मर्यादा आहे आणि प्रत्येक सेकंद कॉलरसाठी शांतता असते. गरज असल्यास डेटाबेस लुकअप करा, पण डाउनस्ट्रीम LLM समकालिकपणे कॉल करू नका — डायनॅमिक prompt जनरेशन हवे असल्यास, आधीच गणना करून कॅश करा.
- **स्वच्छपणे फॉलबॅक करा**. कोणत्याही अनपेक्षित स्थितीत `{}` परत करा, जेणेकरून स्थिरपणे नियुक्त केलेला एजंट कॉल हाताळेल.

<CodeGroup>
```python FastAPI
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
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. प्रतिसाद स्कीमा

प्रतिसादाचा मुख्य भाग
[इनकमिंग-कॉल प्रतिसाद स्कीमाशी](/mr/webhooks/call-incoming)
तंतोतंत जुळतो. सामान्यतः वापरली जाणारी फील्ड्स:

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

<Note>
  प्रति-कॉल बोलण्याचा क्रम आणि `max_hold_seconds` वेबहुक प्रतिसादात उपलब्ध नाहीत.
  तुम्ही संदर्भित केलेल्या
  [एजंटवर](/api-reference/agents) ते सेट करा.
</Note>

### जतन केलेला एजंट ठेवा आणि व्हेरिएबल्स पुरवा

प्रति-कॉल डेटासह त्या संस्थेचा जतन केलेला एजंट वापरण्यासाठी `{"agent_id": 12, "variables": {"name": "Ada"}}` परत करा.
त्याच्या prompt मध्ये `{{name}}` किंवा
`{{name|Friend}}` असू शकते. वेबहुक व्हेरिएबल्स विनंती-स्तरीय व्हेरिएबल्सवर मर्ज होतात; null असल्यास
प्लेसहोल्डरचे डीफॉल्ट वापरले जाते किंवा कोणतेही दिले नसल्यास रिकामा मजकूर वापरला जातो. अंतिम
मूल्ये आणि निराकरण न झालेली नावे कॉल तपशीलांमध्ये आणि पूर्णता वेबहुकमध्ये दिसतात.
जतन केलेल्या एजंटच्या प्रतिसादांमध्ये फक्त `agent_id` आणि `variables` स्वीकारले जातात. `prompt` असल्यास,
प्रतिसाद इनलाइन कॉन्फिगरेशन वापरतो आणि `agent_id` कडे दुर्लक्ष करतो (यात
null किंवा पूर्णांक नसलेला मेटाडेटाही समाविष्ट आहे); इनलाइन prompt तरीही वैध असणे आवश्यक आहे. इनलाइन
कॉन्फिगरेशन प्रतिसादांमध्ये `variables` देखील समाविष्ट असू शकतात. जतन केलेल्या एजंटचे प्रतिसाद
फोन आणि विजेट कॉल्सवर एजंटचा डिप्लॉय केलेला A/B विभाजन वापरतात, त्यानंतर व्हेरिएबल्स रेंडर करतात. [कॉल व्हेरिएबल्स](/mr/guides/call-variables)
मर्यादा आणि सेशन API समर्थनासाठी पहा. ब्लॉकिंग कॉन्फिगरेशन लेगसी
फोन-नंबर/संस्था URL किंवा वेबहुक-मोड विजेट कीमधून येते; एंडपॉइंट-सिस्टम इनकमिंग इव्हेंट्स केवळ सूचना आहेत.

## नमुने

### लॉग-इन केलेल्या वापरकर्त्याचा संदर्भ

वेबहुक-मोड विजेट्समध्ये, व्हिजिटरच्या पेजला ते कोण आहेत हे आधीच
माहित असते. विजेट SDK पुढे पाठवते असा क्वेरी स्ट्रिंग पॅरामीटर वापरून तुमच्या वेबहुकला कॉल करा
(`?customer_id=123`) आणि सर्व्हर-साइडवर ग्राहक शोधा.

### A/B prompt रोलआउट

तुम्ही हे स्वतः तयार करण्यापूर्वी, लक्षात घ्या की ThunderPhone मध्ये अंगभूत
[प्रयोग](/mr/guides/concepts) वैशिष्ट्य आहे
(`/dashboard/experiments` आणि एजंट बिल्डरचा **A/B** टॅब), जे
व्हेरिएंट्स परिभाषित करते, ट्रॅफिक विभाजित करते आणि प्रत्येक व्हेरिएंटचे परिणाम तुलना करते —
वेबहुकची आवश्यकता नाही.

तरीही तुम्हाला वेबहुक-साइड नियंत्रण हवे असल्यास: `call_id` हॅश करा → बकेट;
`0..49` साठी prompt A आणि `50..99` साठी prompt B द्या. तुम्ही निवडलेला
बकेट तुमच्या स्वतःच्या DB मध्ये नोंदवा आणि नंतर पूर्ण झालेल्या कॉलच्या मूल्यांकनाशी त्याचा संबंध जोडा.

### वेळेवर आधारित रूटिंग

व्यावसायिक वेळ → "लाइव्ह सपोर्ट" एजंट; कामकाजानंतरचा वेळ → "मेसेज घ्या"
एजंट. तुमच्या हँडलरमध्ये `new Date().getUTCHours()` वर साधा स्विच वापरा.

---

## पुढील पायऱ्या

<CardGroup cols={2}>
  <Card title="इनकमिंग-कॉल वेबहुक संदर्भ" icon="phone" href="/mr/webhooks/call-incoming">
    प्रत्येक कॉन्फिगरेशन कीसह अचूक विनंती + प्रतिसाद स्कीमा.
  </Card>
  <Card title="वेबहुक स्वाक्षऱ्या पडताळा" icon="shield-check" href="/mr/guides/verify-webhook-signatures">
    HMAC एकदा योग्य करा; सर्वत्र पुन्हा वापरा.
  </Card>
  <Card title="टूल इंटिग्रेशन तयार करा" icon="screwdriver-wrench" href="/mr/guides/build-tool-integration">
    डायनॅमिक रूटिंग प्रति-एजंट टूल्ससोबत एकत्र करा.
  </Card>
  <Card title="वितरण सिमँटिक्स" icon="bolt" href="/mr/webhooks/overview">
    रिट्राय, क्रम, टाइमआउट्स.
  </Card>
</CardGroup>
