Open in
Webhook 概覽
瞭解 ThunderPhone 如何傳送即時事件、如何驗證簽章,以及舊版與端點式傳送模型的比較。
ThunderPhone 會在通話期間發生事件時,向你的伺服器傳送 HTTP POST 請求——例如來電開始、通話結束、評分執行完成、警示觸發等。提供 兩種傳遞
模式:
多個 URL、各端點專屬密鑰、各端點事件篩選條件,
以及自動重試。
透過 GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints 管理。
每個組織一個 URL。承載通話生命週期事件,包括
阻塞式設定交換。透過 GET/PUT /v1/webhook 管理。
事件目錄中列出的所有事件類型,都會透過 webhook 端點
傳遞。六種通話生命週期事件
(telephony.incoming、telephony.complete、telephony.tool、
web.incoming、web.complete、web.tool)也會傳送至
舊版單一 URL webhook——如果你同時設定舊版 URL 與相符的端點,
就會在 兩個路徑上收到事件。阻塞式
行為(telephony.incoming / web.incoming 設定
交換和 webhook 模式的
工具派送)僅存在於舊版路徑;
每次端點傳遞皆為即發即忘的通知。
酬載格式
端點傳遞的 JSON 物件包含 data、event_id 和
type:
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}event_id 對每個發出的事件皆為唯一。它在重試期間
以及所有收到該事件的端點之間都相同——請據此去除重複事件。
舊版單一 URL webhook 會傳送相同的 type 和 data,但
不包含 event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}在傳輸時,每個主體都會以標準化格式序列化——鍵依字母順序排列、 不含空白字元、採用 UTF-8。本文檔中的美化範例僅為提升可讀性。
如需完整的事件類型與酬載欄位清單,請參閱事件目錄。
簽章驗證
每個請求都帶有兩個簽章,兩者皆使用端點的 secret(舊版傳送則使用組織層級 Webhook 的 secret)作為金鑰,以 HMAC-SHA256 產生:
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>會對時間戳記、方法、路徑與查詢字串,以及原始本文進行簽署。請驗證此簽章,並拒絕t與你的時鐘相差超過 5 分鐘 的請求,以避免遭擷取的請求在之後被重新播放。重試請求會在傳送時重新簽署。X-ThunderPhone-Signature僅對原始本文進行簽署。此簽章仍會原封不動地傳送,讓現有的驗證程式可繼續運作。
步驟
- 在進行任何剖析之前,讀取標頭、方法、原始路徑和查詢,以及原始 內文。
- 使用你的密鑰,計算
<t>\n<METHOD>\n<path and query>\n<body>的十六進位 HMAC-SHA256。 - 如果
t的時間偏差超過 300 秒,或結果在固定時間比較中與v1不符,則 拒絕該請求。
內文採用標準 JSON 序列化(鍵已排序、分隔符號緊湊),且我們簽署的位元組與傳送的位元組完全一致。驗證指南提供確切的簽署字串、完整範例、不使用 SDK 的程式碼,以及舊版標頭。
from flask import Flask, abort, request
from thunderphone import verify_signature
app = Flask(__name__)
@app.post("/thunderphone-webhook")
def handle():
query = request.query_string.decode()
if not verify_signature(
request.headers.get("X-ThunderPhone-Signature-V2"),
secret=WEBHOOK_SECRET,
method=request.method,
path=request.path + (f"?{query}" if query else ""),
body=request.get_data(), # raw bytes
):
abort(401)
event = request.get_json()
# dispatch on event["type"] …
return "", 204import express from "express";
import { verifySignature } from "@thunderphone/sdk";
const app = express();
app.post(
"/thunderphone-webhook",
express.raw({ type: "application/json" }),
(req, res) => {
const ok = verifySignature({
header: req.header("X-ThunderPhone-Signature-V2"),
secret: process.env.WEBHOOK_SECRET,
method: req.method,
path: req.originalUrl,
body: req.body,
});
if (!ok) return res.sendStatus(401);
const event = JSON.parse(req.body.toString("utf8"));
// dispatch on event.type …
res.sendStatus(204);
},
);傳遞語意
這些語意適用於 端點 傳送。舊版單一 URL
webhook 是單次同步嘗試,不會重試,唯一例外是電話通話的
telephony.incoming
設定交換,會重試一次。
重試
每個事件都會立即嘗試傳送一次。任何 2xx 回應
都會確認此次傳送。若出現其他結果(非 2xx、
連線錯誤、逾時),我們會在首次嘗試後的 1 分鐘、5 分鐘、30 分鐘、2 小時、6 小時、
12 小時及 24 小時重試——共 8 次嘗試,
橫跨 24 小時。若所有嘗試皆失敗,傳送將停止,且端點
會在
webhook 端點中標記為 status="failing";我們會透過電子郵件通知你組織的
擁有者與管理員,並在控制台中顯示。請在
payload 被可靠接收後儘快回傳 2xx;以非同步方式處理。
從端點測試 API 發出的測試傳送,會在首次嘗試約一分鐘後重試一次,
且絕不會變更端點的狀態。
排序
傳遞排序採盡力而為。在實務上,我們會依事件發出的
順序傳遞,但重試可能會在失敗時重新排序。
請一律依 call_id/物件 ID 去除重複並核對。
重複
傳遞採用至少一次語意:在我們未收到回應後進行的重試,
可能會重複傳遞事件。每次重試都帶有相同的
event_id,因此請儲存已處理的 ID 並略過重複項目。event_id
也會在端點之間共用——訂閱相同事件的兩個端點會收到相同的 event_id。
逾時
端點傳送每次嘗試的逾時時間為 30 秒。在
舊版路徑中,會驅動即時通話行為的阻塞式請求——
telephony.incoming / web.incoming
設定交換——電話通話的首次嘗試限制為 10 秒,
包含其一次重試在內的整個交換限制為 13 秒,
而小工具工作階段則限制為 10 秒。回應緩慢會延遲
接聽通話,因此請盡量在幾秒內回應。Webhook 模式的工具調度
預設允許 20 秒,且工具宣告可設定頂層 timeout。
來源 IP
傳出 webhook 來自 ThunderPhone 的雲端 IP 範圍。 若你的防火牆需要允許清單,請聯絡支援團隊,我們將 提供目前的範圍。
在舊版與端點式 webhook 之間選擇
| 功能 | 舊版 (/v1/webhook) | 端點 (/v1/developer/webhook-endpoints) |
|---|---|---|
| URL 數量 | 每個組織 1 個 | 每個組織多個 |
| 事件涵蓋範圍 | 僅限 telephony.* / web.* | 全部 10 種事件類型 |
| 事件篩選 | —— | 依端點設定 |
| 重試 | 無,電話通話設定交換除外,可重試 1 次 | 24 小時內嘗試 8 次 |
| 封裝格式 | type + data | type + data + event_id |
| 密鑰輪替 | 取代單一密鑰 | 每個端點各有密鑰 |
| 不刪除即可停用 | 使用 PUT /v1/webhook 搭配 {"url": ""} | status=disabled |
| 狀態可見性 | —— | active / disabled / failing |
| 阻塞式設定交換 | 是(telephony.incoming / web.incoming) | 永不——僅傳送通知 |
| 最適合用於 | 動態通話設定 | 在正式環境中處理事件 |
新的整合應透過端點式 webhook 消費事件。只有在你需要於 接聽時動態設定通話,或使用 webhook 模式工具派送時,才應保留(或新增) 舊版 URL——這些請求/回應交換僅會在舊版路徑上執行。