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

Webhooks

telephony.incoming / web.incoming

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

כאשר שיחת טלפון נכנסת מגיעה למספר ללא סוכן שהוקצה לו, או שסשן של וידג'ט אינטרנט מתחיל במפתח ניתן לפרסום במצב mode="webhook", ThunderPhone שולח בקשה חוסמת telephony.incoming / web.incoming אל כתובת ה-webhook הישנה שלכם וממתין לתגובת תצורה: עד 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מספר שלםמזהה שיחה — יציב בכל האירועים של שיחה זו
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מחרוזתקיים כאשר הווידג'ט העביר הקשר דף לכל סשן

שדות הדריסה (language, voice, website_context, וכל variables של הדפדפן) מגיעים רק כאשר מפתח הפרסום מאפשר אותם במסגרת דריסות הדפדפן שלו. בקשת סשן הכוללת דריסה שהמפתח אינו מאפשר נדחית לפני שהוובהוק הזה מופעל.


סכמת תגובה

החזירו אובייקט 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 עם 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 עבור שפת השיחה, כאשר קיים כזה. פועל עבור שיחות טלפון ושיחות בווידג'ט. חל רק על קולות Cartesia של ThunderPhone, ולא על קולות במפתח API משלכם של Cartesia. אינו בשימוש עם agent_id, שבו חלות הגדרות הסוכן השמור
voicemail_actionמחרוזתלאprompt (ברירת מחדל), hangup או message
voicemail_messageמחרוזתלאבשימוש כאשר voicemail_action="message"
agent_nameמחרוזתלאשם תצוגה שמדווח ללוחות המחוונים ולווידג'ט
org_nameמחרוזתלאשם תצוגה של הארגון עבור הדמות של הסוכן
toolsמערךלאסכמות מוטמעות של כלי פונקציה (ראו כלי פונקציה)
transfer_destinationsמערך של אובייקטים | nullלאעד 50 רשומות {name, number} שאליהן שיחה זו יכולה להעביר, המאומתות כמו מספרי ההעברה של סוכן. הסוכן מעביר לפי שם ורק אליהן; [] פירושו שאין העברות. השמיטו שדה זה או שלחו null כדי לאפשר לסוכן להעביר למספר שהוא בוחר
timezoneמחרוזתלאאזור זמן של IANA עבור שיחה זו, כגון America/Chicago. הסוכן מציין את התאריך והשעה בו, לפני אזור הזמן של הסוכן עצמו. מותר גם לצד agent_id. ערכים שאינם אזורי IANA מתעלמים מהם. ראו הגדרת אזור הזמן לכל שיחה
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גבוהה יותרעמוקהמענה ביניים אוטומטי בזמן החשיבה

קשור