प्रत्येक कॉलसाठी डायनॅमिक कॉन्फिगरेशन
डीफॉल्टनुसार प्रत्येक फोन नंबर आणि पब्लिशेबल कीला एक स्थिर एजंट नियुक्त केलेला असतो. तुम्हाला प्रति-कॉलर किंवा प्रति-व्हिजिटर सानुकूलन हवे असल्यास — VIP रूटिंग, लॉग-इन केलेल्या वापरकर्त्याचा संदर्भ, A/B prompt चाचण्या — वेबहुक-मोडवर स्विच करा आणि तुमच्या सर्व्हरला निर्णय घेऊ द्या.
हे कसे कार्य करते
- तुम्ही
telephony.incoming(फोन) किंवाweb.incoming(विजेट) इव्हेंटचे सदस्यत्व घेता. दोन्ही ब्लॉकिंग वेबहुक आहेत: कॉल पुढे सुरू ठेवण्यापूर्वी ThunderPhone तुमच्या प्रतिसादाची 10 सेकंदांपर्यंत वाट पाहते. - ThunderPhone तुम्हाला
{call_id, from_number, to_number}पाठवते (विजेट सेशनमध्ये नंबरऐवजी विजेट-विशिष्ट फील्ड असतात — विनंती स्कीमा पहा). - तुमचा सर्व्हर एजंट कॉन्फिगरेशनसह प्रतिसाद देतो (prompt, आवाज, प्रॉडक्ट, साधने). ThunderPhone कॉलसाठी ते कॉन्फिगरेशन वापरते.
- तुम्ही
{}परत केल्यास, वेळ संपल्यास किंवा त्रुटी आल्यास, स्थिरपणे नियुक्त केलेला एजंट फॉलबॅक म्हणून वापरला जातो. सुरक्षित डीफॉल्ट.
1. वेबहुक गंतव्य कॉन्फिगर करा
फोन कॉल
फोन नंबरसाठी, तुमच्या एंडपॉइंटला telephony.incoming चे सदस्यत्व द्या:
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 असते — ते जतन करा; तुम्ही ते
स्वाक्षरी पडताळणीसाठी वापराल.
वेब विजेट
विजेट सेशनसाठी, तुमच्या एंडपॉइंट URL सह mode="webhook" मध्ये पब्लिशेबल की तयार करा:
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 करेल.
2. हँडलर अंमलात आणा
तीन व्यावहारिक नियम:
- प्रत्येक विनंतीवर स्वाक्षरी सत्यापित करा (पहा वेबहुक स्वाक्षऱ्या सत्यापित करा). डेव्हलपमेंटमध्ये हे वगळू नका — एकदा योग्य पद्धतीने करा आणि पुन्हा वापरा.
- त्वरित प्रतिसाद द्या. दहा सेकंद ही कमाल मर्यादा आहे आणि प्रत्येक सेकंद कॉलरसाठी शांतता असते. गरज असल्यास डेटाबेस लुकअप करा, पण डाउनस्ट्रीम LLM समकालिकपणे कॉल करू नका — डायनॅमिक prompt निर्मिती हवी असल्यास, आधीच गणना करून कॅश करा.
- स्वच्छपणे फॉलबॅक करा. कोणत्याही अनपेक्षित स्थितीत
{}परत करा, जेणेकरून स्थिरपणे नियुक्त केलेला एजंट कॉल हाताळेल.
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
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
},
);
3. प्रतिसाद स्कीमा
प्रतिसाद बॉडी येणाऱ्या कॉलच्या प्रतिसाद स्कीमाशी अगदी जुळते. सामान्यतः वापरली जाणारी फील्ड्स:
| फील्ड | प्रकार | वर्णन |
|---|---|---|
prompt | स्ट्रिंग (आवश्यक) | एजंटसाठी सिस्टम prompt |
voice | स्ट्रिंग (आवश्यक) | GET /v1/voices मधील व्हॉइस ID |
product | स्ट्रिंग | डीफॉल्ट spark आहे |
background_track | स्ट्रिंग | null | परिवेशी ऑडिओ ID |
acknowledgement_prompt_mode | स्ट्रिंग | auto किंवा manual (फक्त अॅक्नॉलेजमेंटसह Storm) |
acknowledgement_prompt | स्ट्रिंग | मोड manual असल्यास आवश्यक |
tools | अॅरे | इनलाइन फंक्शन-टूल स्कीमा — फंक्शन टूल्स पहा |
नमुने
साइन-इन केलेल्या वापरकर्त्याचा संदर्भ
वेबहुक-मोड विजेट्समध्ये, अभ्यागताचे पेज त्यांची ओळख आधीच जाणते.
विजेट SDK फॉरवर्ड करत असलेल्या क्वेरी स्ट्रिंग पॅरामीटरसह (?customer_id=123) तुमच्या वेबहुकला कॉल करा आणि सर्व्हर-साइडवर ग्राहक शोधा.
A/B prompt रोलआउट
तुम्ही हे स्वतः तयार करण्यापूर्वी, लक्षात घ्या की ThunderPhone मध्ये नेटिव्ह
प्रयोग वैशिष्ट्य
(/dashboard/experiments आणि एजंट बिल्डरमधील A/B टॅब) आहे, जे
व्हेरिएंट परिभाषित करते, ट्रॅफिक विभाजित करते आणि प्रत्येक व्हेरिएंटचे परिणाम तुलना करते —
वेबहुकची आवश्यकता नाही.
तरीही तुम्हाला वेबहुक-साइड नियंत्रण हवे असल्यास: call_id हॅश करा → बकेट;
0..49 साठी prompt A आणि 50..99 साठी prompt B द्या. तुम्ही निवडलेले
बकेट तुमच्या स्वतःच्या DB मध्ये नोंदवा आणि नंतर पूर्ण झालेल्या कॉलच्या
मूल्यांकनाशी त्याचा संबंध जोडा.
वेळ-आधारित राउटिंग
व्यवसायाच्या वेळेत → "लाइव्ह सपोर्ट" एजंट; व्यवसायाच्या वेळेनंतर → "संदेश घ्या"
एजंट. तुमच्या हँडलरमध्ये new Date().getUTCHours() वर आधारित साधा स्विच.