ThunderPhone 2.0 đã chính thức ra mắt.Tự thiết lập, từ 2 xu/phút.Xem thông báo ra mắt

Webhooks

telephony.incoming / web.incoming

Webhook chặn định hình cấu hình cuộc gọi đến theo thời gian thực.

Khi một cuộc gọi điện thoại đến số chưa được gán tác nhân AI, hoặc một phiên widget web bắt đầu trên khóa publishable ở mode="webhook", ThunderPhone gửi một yêu cầu telephony.incoming / web.incoming chặn đến URL webhook cũ của bạn và chờ tối đa 10 giây để nhận phản hồi cấu hình. Dùng quy trình này để chọn linh hoạt prompt, giọng nói và công cụ cho từng cuộc gọi — xem hướng dẫn cấu hình cuộc gọi động để biết quy trình đầy đủ.

Quy trình trao đổi chặn không có phương án dự phòng: nếu handler của bạn trả về trạng thái không phải 2xx, hết thời gian chờ hoặc trả về cấu hình không vượt qua xác thực, cuộc gọi sẽ bị từ chối (cuộc gọi điện thoại không kết nối; yêu cầu phiên widget thất bại với 502/422). Phản hồi nhanh — người gọi đang nghe âm báo đổ chuông trong khi bạn quyết định.

Payload yêu cầu

Đối với cuộc gọi điện thoại (telephony.incoming):

{
  "type": "telephony.incoming",
  "data": {
    "call_id":     987654321,
    "from_number": "+14155550199",
    "to_number":   "+15551234567"
  }
}
TrườngKiểuMô tả
call_idintegerID cuộc gọi — không đổi trên mọi sự kiện của cuộc gọi này
from_numberstringSố điện thoại người gọi theo E.164
to_numberstringĐích đến theo E.164 (một trong các số ThunderPhone của bạn)

Đối với phiên widget web (web.incoming), data xác định trang nhúng thay vì số điện thoại:

{
  "type": "web.incoming",
  "data": {
    "call_id": 987654322,
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2"
  }
}
TrườngKiểuMô tả
call_idintegerID cuộc gọi
origin_domainstringOrigin của trang lưu trữ widget
publishable_key_prefixstringCác ký tự đầu tiên của khóa publishable đã mở phiên
language, primary_languagestringCó mặt khi phiên widget yêu cầu ghi đè ngôn ngữ
voicestringCó mặt khi phiên widget yêu cầu ghi đè giọng nói
website_contextstringCó mặt khi widget truyền ngữ cảnh trang cho từng phiên

Lược đồ phản hồi

Trả về một đối tượng JSON mô tả cấu hình tác nhân AI cho cuộc gọi này. promptvoice là bắt buộc; mọi trường khác là tùy chọn.

{
  "prompt":  "You are a helpful booking assistant for Acme Restaurant.",
  "voice":   "john",
  "product": "spark",
  "background_track": null,
  "tools":   []
}
TrườngLoạiBắt buộcMô tả
promptstringSystem prompt điều khiển tác nhân AI
voicestringMã giọng nói từ GET /v1/voices, ví dụ: john. voice_name được chấp nhận làm bí danh. Giọng nói không xác định sẽ không qua xác thực và cuộc gọi bị từ chối
productstringkhôngMặc định là spark. Giá trị được phép: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack
thinking_levelstringkhôngminimal, base (mặc định), hoặc extra. Bị ghi đè đối với sản phẩm Storm: storm-extra* buộc dùng extra, các storm-* khác buộc dùng base
audio_context_modestringkhôngfull (mặc định) hoặc reduced
watchdog_enabledbooleankhôngBật giám sát cho cuộc gọi này. Mặc định là false
additional_audio_contextboolean | nullkhôngBao gồm vài lượt âm thanh gần nhất của người gọi thay vì chỉ lượt gần nhất, giúp cải thiện việc chỉnh sửa và thu thập dữ liệu nhiều đánh vần/số với mức tăng nhỏ về độ trễ/chi phí. Mặc định bật cho phiên cuộc gọi đến và tắt cho cuộc gọi điện thoại đi; null giữ nguyên mặc định
storm_feedback_modestringkhôngnone, acknowledgement (mặc định), hoặc tick
languagestringkhôngCú pháp rút gọn cho primary_language
primary_languagestringkhôngMã ngôn ngữ, được chuẩn hóa (mặc định en). Mã không thể phân giải sẽ từ chối cuộc gọi
has_additional_languagesbooleankhôngMặc định là false
additional_languagesarray of stringkhôngNgôn ngữ bổ sung mà tác nhân AI có thể chuyển sang
native_voice_switchingbooleankhôngMặc định là false. Khi cuộc gọi chuyển sang ngôn ngữ khác, đổi sang giọng nói bản ngữ của ngôn ngữ đó (khớp theo giới tính) thay vì giữ giọng nói đã cấu hình
background_trackstring | nullkhôngMã âm thanh nền hoặc null
acknowledgement_prompt_modestringkhôngauto (mặc định) hoặc manual (sản phẩm Storm-with-ack)
acknowledgement_promptstringkhôngĐược dùng khi acknowledgement_prompt_mode="manual"
silence_interval_secondsinteger | nullkhông5–120. Số giây người gọi im lặng trước khi kiểm tra
silence_max_checkinsinteger | nullkhông1–10
silence_checkins_enabledbooleankhôngMặc định là true
connect_tone_enabledbooleankhôngMặc định là false
voicemail_actionstringkhôngprompt (mặc định), hangup, hoặc message
voicemail_messagestringkhôngĐược dùng khi voicemail_action="message"
agent_namestringkhôngTên hiển thị được báo cáo cho bảng điều khiển và widget
org_namestringkhôngTên hiển thị của tổ chức cho vai trò của tác nhân AI
toolsarraykhôngLược đồ công cụ hàm nội tuyến (xem Công cụ hàm)
call_idintegerkhôngGiá trị phản chiếu tùy chọn của mã cuộc gọi trong yêu cầu; bị bỏ qua

promptvoice là bắt buộc, việc trả về {} hoặc bất kỳ phản hồi nào không qua xác thực sẽ từ chối cuộc gọi với 422 — không có phương án dự phòng tác nhân AI tĩnh trên đường dẫn này (một số hoặc khóa ở chế độ webhook không có tác nhân AI được gán).

Giới hạn kích thước phản hồi


Ví dụ về trình xử lý

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({});
  },
);

Phản hồi kèm công cụ hàm

Đính kèm công cụ để tác nhân AI có thể gọi API của bạn trong khi trò chuyện:

{
  "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"
        }
      }
    }
  ]
}

Bảng tham chiếu nhanh về các gói sản phẩm

Sản phẩmĐộ trễLập luậnXác nhận
sparkThấp nhấtCơ bản
boltThấpCải thiện
storm-baseTrung bìnhMạnh
storm-base-with-ackTrung bìnhMạnhTự động chèn lời đệm khi đang suy nghĩ
storm-extraCao hơnChuyên sâu
storm-extra-with-ackCao hơnChuyên sâuTự động chèn lời đệm khi đang suy nghĩ

Liên quan