ThunderPhone 2.0 正式登場。自助開通,價格低至每分鐘 2¢查看公告

Webhooks

Webhooks 概覽

了解 ThunderPhone 如何傳送即時事件、如何驗證簽名,以及舊版與端點式傳送模式的比較。

ThunderPhone 會在通話期間發生特定事件時,向你的伺服器傳送 HTTP POST 請求——例如開始接聽來電、通話結束、評分執行完成、觸發警示等。系統提供 兩種傳送模式

事件目錄中的全部十種事件類型,都會透過 webhook 端點傳送。六種通話生命週期事件 (telephony.incomingtelephony.completetelephony.toolweb.incomingweb.completeweb.tool)亦會傳送至舊版單一 URL webhook——如果你同時設有舊版 URL 和相符的端點,便會在兩個路徑 收到該事件。阻塞式行為(telephony.incoming / web.incoming 設定 交換及 webhook 模式的 工具分派)只會在舊版路徑上運作; 每次端點傳送均為即發即棄通知。

Payload 格式

端點傳送的內容為包含 dataevent_idtype 的 JSON 物件:

{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}

每個發出的事件都有唯一的 event_id。重試時,以及每個收到該事件的端點之間, 此值均會保持相同——請以此作重複資料排除。

舊版單一 URL webhook 會傳送相同的 typedata,但不會包含 event_id

{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}

在網絡傳輸中,每個主體均會以標準格式序列化——鍵名按字母順序排列、 不含空白字元、採用 UTF-8。本文檔中的美化格式範例僅為方便閱讀。

請參閱事件目錄,了解完整的事件類型及 payload 欄位清單。

簽章驗證

每個請求均會在 X-ThunderPhone-Signature 標頭中附帶涵蓋原始請求內容 的 HMAC-SHA256 簽章。簽署金鑰為端點的 secret(或舊版傳送所使用的組織層級 webhook secret)。

步驟

  1. 在進行任何解析之前讀取原始請求內容。
  2. 計算 hmac_sha256(secret, body).hexdigest()
  3. 以固定時間方式與 X-ThunderPhone-Signature 標頭比較。

我們會為實際傳送的位元組進行簽署,而這些位元組採用標準 JSON 序列化格式(已排序的鍵、精簡分隔符)。因此,根據原始內容驗證一定有效——如你的框架只提供已解析的 JSON,使用已排序的鍵及精簡分隔符重新序列化,便會產生完全相同的位元組。兩種做法均於驗證指南中說明。

Python
import hmac
import hashlib
 
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode("utf-8"),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature or "")
 
# Example Flask handler
from flask import Flask, request, abort
app = Flask(__name__)
 
@app.post("/thunderphone-webhook")
def handle():
    body = request.get_data()
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify_signature(body, sig, WEBHOOK_SECRET):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
Node.js (Express)
import crypto from "node:crypto";
import express from "express";
 
function verifySignature(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  if (!signature || expected.length !== signature.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}
 
const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.header("X-ThunderPhone-Signature") || "";
    if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));
    // dispatch on event.type …
    res.sendStatus(204);
  },
);

傳送語意

以下語意適用於傳送至端點。舊版單一 URL webhook 為單次同步嘗試,並不會重試。

重試

每個事件會立即嘗試傳送一次。任何 2xx 回應 均確認傳送成功。如出現任何其他結果(非 2xx、 連線錯誤、逾時),我們會在首次嘗試後 1 分鐘、5 分鐘、30 分鐘、2 小時、6 小時、 12 小時及 24 小時重試——合共 8 次嘗試, 橫跨 24 小時。如每次嘗試均失敗,傳送將會停止,端點 會在webhook 端點中標示為 status="failing"。payload 一經可靠接收,請立即傳回 2xx; 其後以非同步方式處理。

排序

傳送排序採取盡力而為原則。實際上,我們會按事件發出的 次序傳送,但重試可能會在失敗時改變排序。 請一律按 call_id/物件 id 去除重複項目並進行核對。

重複傳送

傳送採用至少一次語意:在我們未能收到回應後進行的重試, 可能會重複傳送事件。每次重試均會帶有相同的 event_id,因此請儲存已處理的 id 並略過重複項目。event_id 亦會在不同端點之間共用——訂閱同一事件的兩個端點, 會收到相同的 event_id

逾時

每次端點傳送的逾時時間為 30 秒。在舊版路徑中,會影響即時通話行為的 阻塞式請求——telephony.incoming / web.incoming 設定交換——會在 10 秒後逾時;不過,緩慢的回應會延遲接聽來電, 因此建議在數秒內作出回應。Webhook 模式的工具調度 預設允許 20 秒,而工具宣告可設定頂層 timeout

來源 IP

外傳 webhook 來自 ThunderPhone 的雲端 IP 範圍。 如你的防火牆需要允許清單,請聯絡支援團隊,我們會 提供目前的範圍。

選擇舊版或以端點為本的 webhook

功能舊版(/v1/webhook端點(/v1/developer/webhook-endpoints
URL 數量每個組織 1 個每個組織多個
事件涵蓋範圍僅限 telephony.*web.*全部 10 種事件類型
事件篩選按端點設定
重試沒有24 小時內 8 次嘗試
封裝格式type + datatype + data + event_id
密鑰輪換取代單一密鑰每個端點獨立密鑰
不刪除即可停用使用 {"url": ""} 執行 PUT /v1/webhookstatus=disabled
狀態可見度activedisabledfailing
阻塞式設定交換是(telephony.incoming / web.incoming永不——僅限通知
最適合動態通話設定正式環境中的事件處理

新的整合應透過以端點為本的 webhook 處理事件。 只有在你需要於接聽時動態設定通話,或使用 webhook 模式工具調度時, 才應保留(或新增)舊版 URL——這些請求/回應交換只會在舊版路徑上執行。


相關內容