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

Webhooks

telephony.complete / web.complete

Webhook לא חוסם שנשלח כאשר שיחה מסתיימת, עם תמלול, כתובת URL של ההקלטה ומדדים.

אירוע השלמה מופעל לאחר סיום כל שיחה — טלפוניה נכנסת, טלפוניה יוצאת, שיחת אינטרנט או שיחת בדיקה (הפעלת מיקרופון בבונה). הוא אינו חוסם: השיבו עם כל קוד 2xx.

האירוע נמסר בשני הנתיבים:

מטען הבקשה (מסירות לנקודות קצה)

{
  "data": {
    "billable_minutes": 1.25,
    "billing_total_cents": 8,
    "call_id": 987654321,
    "direction": "inbound",
    "duration_seconds": 54,
    "end_reason": "user_hangup",
    "end_time": "2026-04-20T18:25:04.822Z",
    "from_number": "+14155550199",
    "product": "spark",
    "recording_url": "https://storage.example.com/…",
    "start_time": "2026-04-20T18:24:10.113Z",
    "status": "completed",
    "to_number": "+15551234567",
    "transcripts": [ /* see Transcript format */ ],
    "transfer_number": null,
    "voice": "john"
  },
  "event_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
  "type": "telephony.complete"
}
שדהסוגתיאור
call_idמספר שלםיציב בכל אירוע עבור שיחה זו
directionמחרוזתinbound, outbound, web, test. מטענים היסטוריים עשויים להכיל את הערכים הישנים mic או widget
from_number, to_numberמחרוזתE.164. הערך של from_number הוא המחרוזת המילולית "web" עבור שיחות אינטרנט ושיחות בדיקה
origin_domainמחרוזתאינטרנט/בדיקה בלבד — מקור הדף שאירח את הרכיב (ריק עבור הפעלות מיקרופון)
start_time, end_timeחותמת זמןISO 8601 UTC
duration_secondsמספר שלם | nullנגזר מזמני ההתחלה והסיום
statusמחרוזתcompleted או failed
end_reasonמחרוזתראו את הטבלה להלן
product, voiceמחרוזתתצורת הסוכן שהייתה בתוקף בזמן השיחה
transfer_numberמחרוזת | nullמוגדר כאשר השיחה הועברה
recording_urlמחרוזת | nullכתובת URL חתומה עם תפוגה; הורידו בהקדם. null כאשר אין תוצר הקלטה זמין
billable_minutesמספרדקות שחויבו, מעוגלות לרבע הדקה הקרוב ביותר (מרווחים של 15 שניות, מינימום 0.25). שיחות ישירות לתא קולי עדיין מדווחות כאן את דקות המדידה בפועל, אך החיוב מוגבל לדקה אחת לפי תעריף התוכנית.
billing_total_centsמספר שלםסנטים של דולר אמריקאי
transcriptsמערךרשומות תמלול לכל תור; עשוי להיות ריק כאשר תמלול אינו זמין

סיבות סיום

ערךמשמעות
user_hangupהצד המרוחק ניתק ראשון
ai_hangupהבינה המלאכותית סיימה את השיחה במכוון
ai_transferהבינה המלאכותית העבירה את השיחה; transfer_number מוגדר
ai_warm_transferהבינה המלאכותית השלימה העברה חמה (מלווה)
voicemail_hangupזוהה תא קולי והשיחה הסתיימה בהתאם ל-voicemail_action שלכם
max_durationהשיחה הגיעה למגבלת משך הזמן המרבי
supersededההפעלה הוחלפה בהפעלה חדשה יותר
unknownלא ניתן היה לקבוע את סיבת הסיום

פורמט תמלול

כל רשומה ב-transcripts היא תור שיחה אחד. התפקידים הם user (דיבור המתקשר), model (דיבור הסוכן וגם קריאות כלים), tool (תוצאות כלים) ו-system (אירועי שיחה כגון החלפות שפה).

[
  {
    "role": "user",
    "content_type": "text/plain",
    "content": "Hi, I'm calling about my appointment.",
    "start_ms": 1200,
    "end_ms":   4100,
    "audio_url": "https://storage.example.com/…"
  },
  {
    "role": "model",
    "content_type": "text/plain",
    "content": "Sure, what date works best?",
    "start_ms": 4200,
    "end_ms":   6100
  },
  {
    "role": "model",
    "content_type": "application/json",
    "content": {
      "tool_call": "search_appointments",
      "arguments": { "date": "2026-04-21" }
    }
  },
  {
    "role": "tool",
    "content_type": "application/json",
    "content": {
      "tool_name": "search_appointments",
      "response": { "available_slots": ["9:00 AM", "2:00 PM"] }
    }
  }
]
שדהסוגתיאור
roleמחרוזתuser, model, tool או system
content_typeמחרוזתtext/plain לדיבור; application/json לקריאות כלים, לתוצאות כלים ולאירועי מערכת
contentמחרוזת | אובייקטטקסט דיבור, או האובייקט המובנה שמוצג לעיל. קריאות כלים: {"tool_call": name, "arguments": {…}}. תוצאות כלים: {"tool_name": name, "response": {…}}
start_ms, end_msמספר שלםהיסטים מתחילת השיחה, באלפיות שנייה. קיימים כאשר תזמון האודיו ידוע
ttfa_msמספר שלםהזמן עד לאודיו הראשון עבור תור model, כאשר הוא נמדד
audio_url, audio_urlsמחרוזת / מערךכתובות URL חתומות עם תוקף מוגבל לאודיו של התור, כאשר ההקלטה מתבצעת לכל תור בנפרד

להיסטוריית התורים המובנית המלאה (עם סמני הפרעה, הנחיות אישור ומיקומים גולמיים), השתמשו ב- GET /v1/calls/{call_id}/history.

הבדלים במטען נתונים מדור קודם

מעטפת ה-webhook מדור קודם עם כתובת URL יחידה היא {"type": "telephony.complete" | "web.complete", "data": {…}} ללא event_id, וה-data שלה שונה ממטען הנתונים של נקודת הקצה:

  • מערך התורים נמצא תחת history, ולא תחת transcripts (אותה סכמת תורים כמו לעיל).
  • קבוצת השדות היא דוח גולמי של סיום שיחה, ויכולה לכלול שדות פנימיים נוספים מעבר לטבלה לעיל — התייחסו לשדות לא מוכרים כמידע בלבד.
  • שיחות ווב (direction: "web") אינן כוללות את from_number / to_number ומוסיפות את origin_domain.
  • שיחות בדיקת מיקרופון ב-Builder מדווחות כ-telephony.complete בנתיב מדור קודם (מערכת נקודת הקצה ממפה אותן ל-web.complete).
  • תיאום העברה: כאשר שיחה מסתיימת בהעברה, ה-webhook מדור קודם נקרא באופן סינכרוני ועשוי להשיב {"transfer_ready": false} כדי לציין שיעד ההעברה אינו מוכן. כל תגובה אחרת (או היעדר webhook מדור קודם) מאפשרת להעברה להמשיך. מסירות דרך נקודת הקצה לעולם אינן נבדקות לצורך זה.

מטפל לדוגמה

Python (FastAPI)
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, signature: str) -> bool:
    expected = hmac.new(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"] in ("telephony.complete", "web.complete"):
        data = event["data"]
        # Endpoint deliveries use "transcripts"; the legacy webhook uses "history".
        turns = data.get("transcripts") or data.get("history") or []
        await persist_call_record(
            call_id=data["call_id"],
            turns=turns,
            recording_url=data.get("recording_url"),
        )
        if data["end_reason"] in ("ai_transfer", "ai_warm_transfer"):
            await notify_team(data.get("transfer_number"), data["call_id"])
    return {"ok": True}
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" }),
  async (req, res) => {
    if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));
    if (["telephony.complete", "web.complete"].includes(event.type)) {
      const data = event.data;
      // Endpoint deliveries use "transcripts"; the legacy webhook uses "history".
      const turns = data.transcripts ?? data.history ?? [];
      await persistCallRecord({ ...data, turns });
      if (["ai_transfer", "ai_warm_transfer"].includes(data.end_reason)) {
        await notifyTeam(data.transfer_number, data.call_id);
      }
    }
    res.json({ ok: true });
  },
);

תרחישי שימוש נפוצים

שילוב CRM

שמרו את תמלול כל שיחה ואת כתובת ה-URL של ההקלטה לצד רשומות הלקוחות שלכם.

ניתוח נתונים

הזרימו תמלולים למערך לצורך מידול נושאים, חילוץ אותות CSAT או מעקב אחר שיעורי העברה.

סקירת איכות

פתחו שיחות בכלי QA לסקירה אנושית, או העבירו אותן דרך מודל ההערכה שלכם.

התראות

הפעילו התראה לנציג אנושי בעת העברה או כשל.


קשור