Open in
telephony.incoming / web.incoming
ब्लॉकिंग वेबहुक जो रियल टाइम में इनबाउंड कॉल के कॉन्फ़िगरेशन को आकार देता है।
जब कोई इनबाउंड फोन कॉल बिना असाइन किए गए
एजेंट वाले नंबर पर पहुंचती है, या वेब विजेट सेशन
mode="webhook" में किसी पब्लिशेबल की पर शुरू होता है, तो ThunderPhone आपके
लीगेसी वेबहुक URL
को एक ब्लॉकिंग telephony.incoming / web.incoming रिक्वेस्ट भेजता है
और कॉन्फ़िगरेशन रिस्पॉन्स की प्रतीक्षा करता है: फोन कॉल के लिए अधिकतम 13 सेकंड,
जिसमें एक रीट्राई शामिल है, और विजेट सेशन के लिए अधिकतम 10 सेकंड
(टाइमआउट और रीट्राई देखें)। हर कॉल के लिए प्रॉम्प्ट, वॉइस,
और टूल्स को डायनामिक रूप से चुनने के लिए इस एक्सचेंज का उपयोग करें —
एंड-टू-एंड पैटर्न के लिए डायनामिक कॉल कॉन्फ़िगरेशन गाइड
देखें।
टाइमआउट और रिट्राइ
जल्दी जवाब दें: आपके निर्णय लेने के दौरान कॉलर लाइन पर इंतज़ार कर रहा है।
फोन कॉल (telephony.incoming) के लिए:
- पहले प्रयास में 10 सेकंड तक लग सकते हैं।
- अगर टाइमआउट होता है, कनेक्ट नहीं हो पाता, या
5xx,408या429रिस्पॉन्स मिलता है, तो ThunderPhone एक बार फिर कोशिश करता है। रिट्राइ को पूरे एक्सचेंज के लिए 13-सेकंड के बजट में बचा हुआ समय मिलता है: टाइमआउट हुए पहले प्रयास के बाद लगभग 3 सेकंड, तेज़ विफलता के बाद लगभग पूरे 13 सेकंड। अगर 2 सेकंड से कम समय बचता है, तो रिट्राइ नहीं किया जाता। - कोई भी अन्य
4xxरिस्पॉन्स, साइज़ सीमा से बड़ा रिस्पॉन्स, या ऐसा2xxजिसका बॉडी मान्य कॉन्फ़िगरेशन नहीं है, अंतिम माना जाता है और उस पर रिट्राइ नहीं होता। - दोनों प्रयास एक ही बॉडी, एक ही
X-ThunderPhone-Signature, और एक हीX-ThunderPhone-Idempotency-Keyहेडर (telephony-incoming-<call_id>) भेजते हैं, इसलिए साइड इफेक्ट्स वाला हैंडलर रिट्राइ को पहचान सकता है।
कोई फॉलबैक एजेंट नहीं है। अगर उपयोग योग्य कॉन्फ़िगरेशन नहीं मिलता, तो कॉलर को साइलेंट लाइन पर छोड़ने के बजाय कॉल तुरंत समाप्त कर दी जाती है। यह कॉल हिस्ट्री में end_reason inbound_config_webhook_failed के साथ एक असफल, बिना बिल वाली कॉल के रूप में दिखाई देती है, और सामान्य
telephony.complete इवेंट भेजा जाता है।
विजेट सेशन (web.incoming) को 10 सेकंड तक का केवल एक प्रयास मिलता है। अगर यह विफल होता है, तो सेशन रिक्वेस्ट 502 या 422 के साथ विफल होती है।
रिक्वेस्ट पेलोड
फ़ोन कॉल (telephony.incoming) के लिए:
{
"type": "telephony.incoming",
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}| फ़ील्ड | टाइप | विवरण |
|---|---|---|
call_id | integer | कॉल id — इस कॉल के सभी इवेंट्स में स्थिर |
from_number | string | E.164 कॉलर नंबर |
to_number | string | E.164 डेस्टिनेशन (आपके ThunderPhone नंबरों में से एक) |
वेब विजेट सेशन (web.incoming) के लिए, data फ़ोन नंबरों के बजाय
एम्बेडिंग पेज की पहचान करता है:
{
"type": "web.incoming",
"data": {
"call_id": 987654322,
"origin_domain": "https://example.com",
"publishable_key_prefix": "pk_live_a1b2"
}
}| फ़ील्ड | टाइप | विवरण |
|---|---|---|
call_id | इंटीजर | कॉल आईडी |
origin_domain | स्ट्रिंग | विजेट होस्ट करने वाले पेज का ओरिजिन, जैसा ब्राउज़र ने भेजा है। कोई नॉन-ब्राउज़र क्लाइंट कोई भी वैल्यू भेज सकता है, इसलिए इसे इस बात के प्रमाण के रूप में न मानें कि रिक्वेस्ट कहाँ से आई है |
publishable_key_prefix | स्ट्रिंग | सेशन खोलने वाली publishable key के शुरुआती कैरेक्टर |
language, primary_language | स्ट्रिंग | विजेट सेशन द्वारा भाषा ओवरराइड का अनुरोध करने पर मौजूद होता है |
voice | स्ट्रिंग | विजेट सेशन द्वारा वॉइस ओवरराइड का अनुरोध करने पर मौजूद होता है |
website_context | स्ट्रिंग | विजेट द्वारा प्रति-सेशन पेज कॉन्टेक्स्ट भेजने पर मौजूद होता है |
ओवरराइड फ़ील्ड (language, voice, website_context, और कोई भी
ब्राउज़र variables) केवल तब आते हैं जब publishable key उन्हें अपने
ब्राउज़र ओवरराइड्स में अनुमति देती है। ऐसा सेशन रिक्वेस्ट जिसमें ऐसी कोई फ़ील्ड हो जिसकी key अनुमति नहीं देती, इस webhook के कॉल होने से पहले अस्वीकार कर दिया जाता है।
रिस्पॉन्स स्कीमा
इस कॉल के लिए एजेंट कॉन्फ़िगरेशन का वर्णन करने वाला JSON ऑब्जेक्ट लौटाएँ।
prompt और voice आवश्यक हैं; बाकी सब वैकल्पिक है।
{
"prompt": "You are a helpful booking assistant for Acme Restaurant.",
"voice": "john",
"product": "spark",
"background_track": null,
"tools": []
}| फ़ील्ड | प्रकार | आवश्यक | विवरण |
|---|---|---|---|
prompt | स्ट्रिंग | हाँ | एजेंट को संचालित करने वाला सिस्टम प्रॉम्प्ट |
voice | स्ट्रिंग | हाँ | GET /v1/voices से वॉइस आईडी, उदाहरण के लिए john। voice_name को उपनाम के रूप में स्वीकार किया जाता है। अज्ञात वॉइस वेलिडेशन में विफल होती हैं और कॉल अस्वीकार कर देती हैं |
product | स्ट्रिंग | नहीं | डिफ़ॉल्ट रूप से spark। अनुमत: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack |
thinking_level | स्ट्रिंग | नहीं | minimal, base (डिफ़ॉल्ट), या extra। Storm उत्पादों के लिए ओवरराइड किया जाता है: storm-extra* extra को बाध्य करता है, अन्य storm-* base को बाध्य करते हैं |
audio_context_mode | स्ट्रिंग | नहीं | full (डिफ़ॉल्ट) या reduced |
watchdog_enabled | बूलियन | नहीं | इस कॉल के लिए सुपरविजन सक्षम करें। डिफ़ॉल्ट false |
additional_audio_context | बूलियन | null | नहीं | केवल सबसे हालिया टर्न के बजाय कॉलर ऑडियो के पिछले कुछ टर्न शामिल करें, जिससे कम लेटेंसी/लागत ओवरहेड पर सुधार और वर्तनी/संख्या-प्रधान डेटा कलेक्शन बेहतर होता है। इनबाउंड सेशनों के लिए डिफ़ॉल्ट रूप से चालू और आउटबाउंड फ़ोन कॉल के लिए बंद; null डिफ़ॉल्ट बनाए रखता है |
storm_feedback_mode | स्ट्रिंग | नहीं | none, acknowledgement (डिफ़ॉल्ट), या tick |
language | स्ट्रिंग | नहीं | primary_language का संक्षिप्त रूप |
primary_language | स्ट्रिंग | नहीं | भाषा कोड, नॉर्मलाइज़्ड (डिफ़ॉल्ट en)। जिन कोड का समाधान नहीं हो सकता, वे कॉल अस्वीकार कर देते हैं |
has_additional_languages | बूलियन | नहीं | डिफ़ॉल्ट false |
additional_languages | स्ट्रिंग की ऐरे | नहीं | अतिरिक्त भाषाएँ जिनमें एजेंट स्विच कर सकता है |
native_voice_switching | बूलियन | नहीं | डिफ़ॉल्ट false। जब कॉल किसी अन्य भाषा में स्विच होती है, तो कॉन्फ़िगर की गई वॉइस रखने के बजाय उस भाषा की मूल वॉइस पर स्विच करें (लिंग के अनुसार मिलान किया गया) |
language_switching | स्ट्रिंग | null | नहीं | ask_first (डिफ़ॉल्ट) या follow_caller; null, ask_first के समान है। बहुभाषी एजेंट के लिए यह निर्धारित करता है कि दूसरा व्यक्ति उसकी किसी अन्य भाषा में बोले तो कैसे संभालना है। ask_first भाषा का अनुरोध करने या उसमें पूरा वाक्य बोलने पर तुरंत स्विच करता है, और “हाँ” जैसी छोटी बात के बाद पूछता है कि क्या उसी भाषा में जारी रखना है। follow_caller उनके द्वारा बोली गई किसी भी कॉन्फ़िगर की गई भाषा में स्विच करता है, भले ही वे केवल एक शब्द बोलें। केवल तब लागू होता है जब additional_languages सेट हो |
background_track | स्ट्रिंग | null | नहीं | एम्बिएंट ऑडियो आईडी या null |
acknowledgement_prompt_mode | स्ट्रिंग | नहीं | auto (डिफ़ॉल्ट) या manual (Storm-with-ack उत्पाद) |
acknowledgement_prompt | स्ट्रिंग | नहीं | acknowledgement_prompt_mode="manual" होने पर उपयोग किया जाता है |
silence_interval_seconds | इंटीजर | null | नहीं | 5–120। चेक-इन से पहले कॉलर की चुप्पी के सेकंड |
silence_max_checkins | इंटीजर | null | नहीं | 1–10 |
silence_checkins_enabled | बूलियन | नहीं | डिफ़ॉल्ट true |
connect_tone_enabled | बूलियन | नहीं | डिफ़ॉल्ट false |
pronunciation_dicts | बूलियन | null | नहीं | डिफ़ॉल्ट false; null, false के समान है। true होने पर, कॉल का Cartesia स्पीच कॉल की भाषा के लिए ThunderPhone की उच्चारण डिक्शनरी का उपयोग करता है, जहाँ उपलब्ध हो। फ़ोन और विजेट कॉल के लिए काम करता है। केवल ThunderPhone की अपनी Cartesia वॉइस पर लागू होता है, आपके स्वयं के Cartesia API key की वॉइस पर नहीं। agent_id के साथ उपयोग नहीं होता, जहाँ सेव किए गए एजेंट की सेटिंग्स लागू होती हैं |
voicemail_action | स्ट्रिंग | नहीं | prompt (डिफ़ॉल्ट), hangup, या message |
voicemail_message | स्ट्रिंग | नहीं | voicemail_action="message" होने पर उपयोग किया जाता है |
agent_name | स्ट्रिंग | नहीं | डैशबोर्ड्स और विजेट में रिपोर्ट किया गया डिस्प्ले नाम |
org_name | स्ट्रिंग | नहीं | एजेंट के पर्सोना के लिए संगठन का डिस्प्ले नाम |
tools | ऐरे | नहीं | इनलाइन फ़ंक्शन-टूल स्कीमा (Function Tools देखें) |
transfer_destinations | ऑब्जेक्ट की ऐरे | null | नहीं | इस कॉल को ट्रांसफ़र किए जा सकने वाले अधिकतम 50 {name, number} एंट्री, जिन्हें एजेंट के ट्रांसफ़र नंबरों की तरह वेलिडेट किया जाता है। एजेंट नाम से और केवल इन्हीं पर ट्रांसफ़र करता है; [] का अर्थ है कोई ट्रांसफ़र नहीं। एजेंट को अपने चुने नंबर पर ट्रांसफ़र करने देने के लिए इसे छोड़ दें या null भेजें |
timezone | स्ट्रिंग | नहीं | इस कॉल के लिए IANA टाइम ज़ोन, जैसे America/Chicago। एजेंट अपनी स्वयं की टाइम ज़ोन के बजाय इसमें तारीख और समय बताता है। agent_id के साथ भी अनुमत है। जो मान IANA ज़ोन नहीं हैं, उन्हें अनदेखा किया जाता है। हर कॉल के लिए टाइम ज़ोन सेट करें देखें |
call_id | इंटीजर | नहीं | अनुरोध की कॉल आईडी का वैकल्पिक इको; अनदेखा किया जाता है |
क्योंकि prompt और voice आवश्यक हैं, {} या वेलिडेशन में विफल कोई भी
रिस्पॉन्स लौटाने पर कॉल अस्वीकार हो जाती है: फोन कॉल
टाइमआउट और रिट्राई में बताए अनुसार समाप्त हो जाती है, और
विजेट सेशन अनुरोध 422 के साथ विफल होता है। इस पाथ पर कोई स्टैटिक-एजेंट
फॉलबैक नहीं है (वेबहुक मोड में किसी नंबर या की के लिए कोई एजेंट असाइन नहीं
होता)।
रिस्पॉन्स आकार सीमा
उदाहरण हैंडलर
import hashlib
import hmac
import json
import os
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
WEBHOOK_SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]
def verify(body: bytes, signature: str) -> bool:
expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")
@app.post("/thunderphone-webhook")
async def webhook(request: Request):
body = await request.body()
if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
raise HTTPException(status_code=401)
event = json.loads(body)
if event["type"] == "telephony.incoming":
caller = event["data"]["from_number"]
prompt = (
"Greet the caller as a San Francisco local…"
if caller.startswith("+1415")
else "You are a friendly customer support agent…"
)
return {
"prompt": prompt,
"voice": "john",
"product": "spark",
}
if event["type"] == "web.incoming":
return {
"prompt": "You are the website's helpful voice assistant…",
"voice": "john",
"product": "spark",
}
return {}import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;
function verify(body, signature) {
const expected = crypto
.createHmac("sha256", SECRET)
.update(body)
.digest("hex");
return signature &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
app.post(
"/thunderphone-webhook",
express.raw({ type: "application/json" }),
(req, res) => {
if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
if (event.type === "telephony.incoming" || event.type === "web.incoming") {
const caller = event.data.from_number || "web";
const prompt = caller.startsWith("+1415")
? "Greet the caller as a San Francisco local…"
: "You are a friendly customer support agent…";
return res.json({
prompt,
voice: "john",
product: "spark",
});
}
res.json({});
},
);फंक्शन टूल्स के साथ रिस्पॉन्स
टूल्स अटैच करें ताकि AI बातचीत के बीच में आपके API को कॉल कर सके:
{
"prompt": "You are a booking assistant. Use the available tools to help customers schedule appointments.",
"voice": "john",
"product": "spark",
"tools": [
{
"type": "function",
"function": {
"name": "search_appointments",
"description": "Find available appointment slots",
"parameters": {
"type": "object",
"properties": {
"date": { "type": "string", "description": "YYYY-MM-DD" },
"service": { "type": "string" }
},
"required": ["date"]
}
},
"endpoint": {
"url": "https://api.example.com/appointments/search",
"method": "POST",
"headers": {
"X-Api-Key": "your-key"
}
}
}
]
}प्रोडक्ट टियर चीट शीट
| प्रोडक्ट | लेटेंसी | रीजनिंग | स्वीकृति |
|---|---|---|---|
spark | सबसे कम | बेसिक | — |
bolt | कम | बेहतर | — |
storm-base | मध्यम | मजबूत | — |
storm-base-with-ack | मध्यम | मजबूत | सोचते समय ऑटो फिलर |
storm-extra | अधिक | गहन | — |
storm-extra-with-ack | अधिक | गहन | सोचते समय ऑटो फिलर |
संबंधित
नॉन-ब्लॉकिंग कॉल-समाप्ति इवेंट।
tools[] और साइन किए गए एंडपॉइंट कॉन्ट्रैक्ट के लिए पूर्ण JSON स्कीमा।
telephony.incoming / web.incoming के लिए कई URLs सब्सक्राइब करें।
प्रति-कॉलर प्रॉम्प्ट्स, टूल्स और A/B टेस्ट के पैटर्न।