ThunderPhone 2.0 अब लाइव है।सेल्फ़-सर्व, 2¢ प्रति मिनट से शुरू।घोषणा पढ़ें

Webhooks

telephony.incoming / web.incoming

ब्लॉकिंग वेबहुक जो रियल टाइम में इनबाउंड कॉल के कॉन्फ़िगरेशन को आकार देता है।

जब किसी इनबाउंड फ़ोन कॉल का नंबर बिना असाइन किए गए एजेंट के होता है, या किसी publishable key पर mode="webhook" में वेब विजेट सेशन शुरू होता है, तो ThunderPhone आपके लेगेसी वेबहुक URL पर एक ब्लॉकिंग telephony.incoming / web.incoming रिक्वेस्ट भेजता है और कॉन्फ़िगरेशन रिस्पॉन्स के लिए 10 सेकंड तक प्रतीक्षा करता है। हर कॉल के लिए प्रॉम्प्ट, वॉइस और टूल्स को डायनामिक रूप से चुनने के लिए इस एक्सचेंज का उपयोग करें — पूरे पैटर्न के लिए डायनामिक कॉल कॉन्फ़िगरेशन गाइड देखें।

इस ब्लॉकिंग एक्सचेंज का कोई फ़ॉलबैक नहीं है: यदि आपका हैंडलर non-2xx स्टेटस लौटाता है, टाइम आउट होता है, या ऐसा कॉन्फ़िग लौटाता है जो वैलिडेशन में विफल हो जाता है, तो कॉल अस्वीकार कर दी जाती है (फ़ोन कॉल कनेक्ट नहीं होती; विजेट सेशन रिक्वेस्ट 502/422 के साथ विफल होती है)। तुरंत जवाब दें — जब आप निर्णय ले रहे होते हैं, तब कॉलर रिंगबैक सुन रहा होता है।

रिक्वेस्ट पेलोड

फ़ोन कॉल (telephony.incoming) के लिए:

{
  "type": "telephony.incoming",
  "data": {
    "call_id":     987654321,
    "from_number": "+14155550199",
    "to_number":   "+15551234567"
  }
}
फ़ील्डटाइपविवरण
call_idintegerकॉल id — इस कॉल के सभी इवेंट्स में स्थिर
from_numberstringE.164 कॉलर नंबर
to_numberstringE.164 डेस्टिनेशन (आपके ThunderPhone नंबरों में से एक)

वेब विजेट सेशन (web.incoming) के लिए, data फ़ोन नंबरों के बजाय एम्बेडिंग पेज की पहचान करता है:

{
  "type": "web.incoming",
  "data": {
    "call_id": 987654322,
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2"
  }
}
फ़ील्डटाइपविवरण
call_idintegerकॉल id
origin_domainstringविजेट होस्ट करने वाले पेज का ओरिजिन
publishable_key_prefixstringसेशन खोलने वाली publishable key के शुरुआती कैरेक्टर
language, primary_languagestringजब विजेट सेशन ने भाषा ओवरराइड का अनुरोध किया हो, तब मौजूद
voicestringजब विजेट सेशन ने वॉइस ओवरराइड का अनुरोध किया हो, तब मौजूद
website_contextstringजब विजेट ने प्रति-सेशन पेज कॉन्टेक्स्ट पास किया हो, तब मौजूद

रिस्पॉन्स स्कीमा

इस कॉल के लिए एजेंट कॉन्फ़िगरेशन का वर्णन करने वाला 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 से वॉइस आईडी, जैसे johnvoice_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। जब कॉल किसी अन्य भाषा पर स्विच होती है, तो कॉन्फ़िगर की गई वॉइस रखने के बजाय उस भाषा की नेटिव वॉइस पर स्विच करें (जेंडर के अनुसार मैच की गई)
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
voicemail_actionस्ट्रिंगनहींprompt (डिफ़ॉल्ट), hangup, या message
voicemail_messageस्ट्रिंगनहींvoicemail_action="message" होने पर उपयोग किया जाता है
agent_nameस्ट्रिंगनहींडैशबोर्ड और विजेट को रिपोर्ट किया जाने वाला डिस्प्ले नाम
org_nameस्ट्रिंगनहींएजेंट के पर्सोना के लिए संगठन का डिस्प्ले नाम
toolsऐरेनहींइनलाइन फ़ंक्शन-टूल स्कीमा (Function Tools देखें)
call_idइंटीजरनहींरिक्वेस्ट की कॉल आईडी का वैकल्पिक इको; अनदेखा किया जाता है

क्योंकि prompt और voice आवश्यक हैं, {} या वैलिडेशन में विफल होने वाला कोई भी रिस्पॉन्स कॉल को 422 के साथ अस्वीकार कर देता है — इस पाथ पर कोई स्टैटिक-एजेंट फ़ॉलबैक नहीं है (वेबहुक मोड में किसी नंबर या की को कोई एजेंट असाइन नहीं होता)।


रिस्पॉन्स आकार सीमा


उदाहरण हैंडलर

Python (FastAPI)
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 {}
Node.js (Express)
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अधिकगहनसोचते समय ऑटो फिलर

संबंधित