ThunderPhone 2.0 באוויר.בשירות עצמי, החל מ-2¢/דקה.קראו את הודעת ההשקה

Webhooks

telephony.incoming / web.incoming

Webhook חוסם שמעצב את תצורת השיחה הנכנסת בזמן אמת.

כאשר שיחת טלפון נכנסת מגיעה למספר ללא סוכן משויך, או שסשן של וידג'ט אינטרנט מתחיל במפתח ניתן לפרסום במצב mode="webhook", ‏ThunderPhone שולחת בקשת telephony.incoming / web.incoming חוסמת אל כתובת הוובהוק הישנה שלכם וממתינה עד 10 שניות לתשובת תצורה. השתמשו בהחלפה זו כדי לבחור באופן דינמי הנחיה, קול וכלים לכל שיחה — עיינו במדריך לתצורת שיחות דינמית לדפוס המלא מקצה לקצה.

להחלפה החוסמת אין חלופה: אם המטפל שלכם מחזיר סטטוס שאינו 2xx, פג תוקף הזמן שלו, או שהוא מחזיר תצורה שנכשלת באימות, השיחה נדחית (שיחת הטלפון אינה מתחברת; בקשת סשן הווידג'ט נכשלת עם 502/422). השיבו במהירות — המתקשר שומע צליל חיוג בזמן שאתם מחליטים.

מטען הבקשה

עבור שיחות טלפון (telephony.incoming):

{
  "type": "telephony.incoming",
  "data": {
    "call_id":     987654321,
    "from_number": "+14155550199",
    "to_number":   "+15551234567"
  }
}
שדהסוגתיאור
call_idמספר שלםמזהה שיחה — יציב בכל האירועים של שיחה זו
from_numberמחרוזתמספר המתקשר בפורמט E.164
to_numberמחרוזתיעד בפורמט 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מחרוזתהתווים הראשונים של המפתח הניתן לפרסום שפתח את הסשן
language, primary_languageמחרוזתמופיע כאשר סשן הווידג'ט ביקש עקיפת שפה
voiceמחרוזתמופיע כאשר סשן הווידג'ט ביקש עקיפת קול
website_contextמחרוזתמופיע כאשר הווידג'ט העביר הקשר עמוד לכל סשן

סכמת תגובה

החזירו אובייקט JSON המתאר את תצורת הסוכן עבור שיחה זו. prompt ו-voice נדרשים; כל היתר אופציונלי.

{
  "prompt":  "You are a helpful booking assistant for Acme Restaurant.",
  "voice":   "john",
  "product": "spark",
  "background_track": null,
  "tools":   []
}
שדהסוגנדרשתיאור
promptstringכןהנחיית מערכת שמנחה את הסוכן
voicestringכןמזהה קול מתוך GET /v1/voices, לדוגמה john. ‏voice_name מתקבל כשם חלופי. קולות לא מוכרים נכשלים באימות ודוחים את השיחה
productstringלאברירת המחדל היא spark. הערכים המותרים: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack
thinking_levelstringלאminimal, ‏base (ברירת מחדל), או extra. מוחלף עבור מוצרי Storm: ‏storm-extra* כופה extra, ומוצרי storm-* אחרים כופים base
audio_context_modestringלאfull (ברירת מחדל) או reduced
watchdog_enabledbooleanלאהפעלת פיקוח עבור שיחה זו. ברירת המחדל היא false
additional_audio_contextboolean | nullלאהכללת הסבבים האחרונים של אודיו המתקשר במקום הסבב האחרון בלבד, לשיפור תיקונים ואיסוף נתונים עתיר איות ומספרים, בתוספת קטנה של שיהוי ועלות. מופעל כברירת מחדל עבור סשנים נכנסים ומושבת עבור שיחות טלפון יוצאות; null שומר על ברירת המחדל
storm_feedback_modestringלאnone, ‏acknowledgement (ברירת מחדל), או tick
languagestringלאקיצור עבור primary_language
primary_languagestringלאקוד שפה, מנורמל (ברירת המחדל en). קודים שלא ניתן לפתור דוחים את השיחה
has_additional_languagesbooleanלאברירת המחדל היא false
additional_languagesarray of stringלאשפות נוספות שהסוכן יכול לעבור אליהן
native_voice_switchingbooleanלאברירת המחדל היא false. כאשר השיחה עוברת לשפה אחרת, החליפו לקול שמקורו בשפה זו (מותאם לפי מגדר) במקום לשמור על הקול שהוגדר
background_trackstring | nullלאמזהה אודיו סביבתי או null
acknowledgement_prompt_modestringלאauto (ברירת מחדל) או manual (מוצרי Storm-with-ack)
acknowledgement_promptstringלאמשמש כאשר acknowledgement_prompt_mode="manual"
silence_interval_secondsinteger | nullלא5–120. שניות של שקט מצד המתקשר לפני בדיקה
silence_max_checkinsinteger | nullלא1–10
silence_checkins_enabledbooleanלאברירת המחדל היא true
connect_tone_enabledbooleanלאברירת המחדל היא false
voicemail_actionstringלאprompt (ברירת מחדל), hangup, או message
voicemail_messagestringלאמשמש כאשר voicemail_action="message"
agent_namestringלאשם תצוגה המדווח ללוחות הבקרה ולווידג'ט
org_namestringלאשם התצוגה של הארגון עבור דמות הסוכן
toolsarrayלאסכמות של כלי פונקציה מוטבעים (ראו כלי פונקציה)
call_idintegerלאהד אופציונלי של מזהה השיחה בבקשה; מתעלמים ממנו

מכיוון ש-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גבוהה יותרעמוקהמענה ביניים אוטומטי בזמן החשיבה

קשור