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

Webhooks

Webhooks 概覽

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

ThunderPhone 會在通話期間發生事件時,向你的伺服器傳送 HTTP POST 請求——例如入線通話開始、通話結束、評分執行完成、警示觸發等。目前提供 兩種傳送模式:

事件目錄中列出的所有事件類型,均會透過 Webhook 端點傳送。六種通話生命週期事件 (telephony.incoming、telephony.complete、telephony.tool、 web.incoming、web.complete、web.tool)亦會傳送至舊版單一 URL Webhook——如你同時設定舊版 URL 及相符的端點,便會在兩個路徑收到該事件。阻塞式行為(telephony.incoming / web.incoming 設定交換及 Webhook 模式的工具派送)只存在於舊版路徑;所有端點傳送均為發送後不等待回應的通知。

Payload 格式

端點傳送的內容為包含 data、event_id 及 type 的 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 會傳送相同的 type 和 data,但不包含 event_id:

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

在實際傳輸時,每個內容均會以標準格式序列化——鍵按英文字母排序、不含空白字元、採用 UTF-8。本文件中的格式化範例僅供提升可讀性。

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

簽署驗證

每個請求均附有兩個簽名,兩者均以端點的 secret(或舊版傳送所用的機構層級 webhook secret)作為金鑰,採用 HMAC-SHA256:

  • X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex> 會簽署 時間戳記、方法、路徑及查詢參數,以及原始請求主體。請驗證 此簽名,並拒絕 t 與你的時鐘相差超過 5 分鐘 的請求, 以防截取的請求日後被重放。重試請求會在傳送時重新簽名。
  • X-ThunderPhone-Signature 只會簽署原始請求主體。此簽名仍會以 不變的方式傳送,讓現有驗證器繼續正常運作。

步驟

  1. 在進行任何剖析之前,讀取標頭、方法、原始路徑及查詢字串,以及原始 主體內容。
  2. 使用你的密鑰,計算 <t>\n<METHOD>\n<path and query>\n<body> 的十六進位 HMAC-SHA256。
  3. 如 t 相差超過 300 秒,或結果在恆定時間比較中與 v1 不符,便拒絕該請求。

主體內容採用標準 JSON 序列化格式(鍵已排序、分隔符號精簡),而我們簽署的正是所傳送的位元組。驗證指南提供確切的已簽署字串、完整範例、不使用 SDK 的程式碼,以及舊版標頭。

Python (Flask)
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 "", 204
Node.js (Express)
import 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";我們會以電郵通知你所屬機構的 擁有人及管理員,並在控制台顯示。有效載荷一經可靠接收,請盡快回傳 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 種事件類型
事件篩選—按端點設定
重試沒有,惟電話通話設定交換可重試一次24 小時內嘗試 8 次
封裝格式type + datatype + data + event_id
密鑰輪換取代單一密鑰按端點設定密鑰
停用而不刪除使用 PUT /v1/webhook 並附上 {"url": ""}status=disabled
狀態可見性—active / disabled / failing
阻塞式設定交換是(telephony.incoming / web.incoming)永不——僅作通知
最適合動態通話設定在正式環境中接收事件

新的整合應透過端點式 webhook 接收事件。只有在你於 接聽來電時動態設定通話,或使用 webhook 模式工具派送時, 才應保留(或新增)舊版 URL——這些請求/回應交換僅會在舊版路徑上執行。


相關資源