---
title: "Webhook 概覽"
description: "瞭解 ThunderPhone 如何傳送即時事件、如何驗證簽章，以及舊版與端點式傳送模型的比較。"
---

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

<CardGroup cols={2}>
  <Card title="Webhook 端點（建議使用）" icon="bolt" href="/zh-Hant/webhooks/endpoints">
    多個 URL、各端點專屬密鑰、各端點事件篩選條件，
    以及自動重試。
    透過 `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints` 管理。
  </Card>
  <Card title="單一 URL 舊版 webhook" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    每個組織一個 URL。承載通話生命週期事件，包括
    **阻塞式**設定交換。透過 `GET/PUT /v1/webhook` 管理。
  </Card>
</CardGroup>

[事件目錄](/zh-Hant/webhooks/events)中列出的所有事件類型，都會透過 webhook 端點
傳遞。六種通話生命週期事件
（`telephony.incoming`、`telephony.complete`、`telephony.tool`、
`web.incoming`、`web.complete`、`web.tool`）也會傳送至
舊版單一 URL webhook——如果你同時設定舊版 URL 與相符的端點，
就會在 **兩個**路徑上收到事件。阻塞式
行為（[`telephony.incoming` / `web.incoming` 設定
交換](/zh-Hant/webhooks/call-incoming)和 webhook 模式的
[工具派送](/zh-Hant/tools/overview)）僅存在於舊版路徑；
每次端點傳遞皆為即發即忘的通知。

## 酬載格式

端點傳遞的 JSON 物件包含 `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`：

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

在傳輸時，每個主體都會以標準化格式序列化——鍵依字母順序排列、
不含空白字元、採用 UTF-8。本文檔中的美化範例僅為提升可讀性。

如需完整的事件類型與酬載欄位清單，請參閱[事件目錄](/zh-Hant/webhooks/events)。

## 簽章驗證

每個請求都帶有兩個簽章，兩者皆使用端點的 `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 序列化（鍵已排序、分隔符號緊湊），且我們簽署的位元組與傳送的位元組完全一致。[驗證指南](/zh-Hant/guides/verify-webhook-signatures)提供確切的簽署字串、完整範例、不使用 SDK 的程式碼，以及舊版標頭。

<CodeGroup>
```python 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
```

```javascript 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);
  },
);
```
</CodeGroup>

## 傳遞語意

這些語意適用於 **端點** 傳送。舊版單一 URL
webhook 是單次同步嘗試，不會重試，唯一例外是電話通話的
[`telephony.incoming`](/zh-Hant/webhooks/call-incoming)
設定交換，會重試一次。

<AccordionGroup>
  <Accordion title="重試">
    每個事件都會立即嘗試傳送一次。任何 `2xx` 回應
    都會確認此次傳送。若出現其他結果（非 2xx、
    連線錯誤、逾時），我們會在**首次嘗試後的 1 分鐘、5 分鐘、30 分鐘、2 小時、6 小時、
    12 小時及 24 小時**重試——共 8 次嘗試，
    橫跨 24 小時。若所有嘗試皆失敗，傳送將停止，且端點
    會在
    [webhook 端點](/zh-Hant/webhooks/endpoints)中標記為 `status="failing"`；我們會透過電子郵件通知你組織的
    擁有者與管理員，並在控制台中顯示。請在
    payload 被可靠接收後儘快回傳 `2xx`；以非同步方式處理。
    從端點測試 API 發出的測試傳送，會在首次嘗試約一分鐘後重試一次，
    且絕不會變更端點的狀態。
  </Accordion>

  <Accordion title="排序">
    傳遞排序採盡力而為。在實務上，我們會依事件發出的
    順序傳遞，但重試可能會在失敗時重新排序。
    請一律依 `call_id`／物件 ID 去除重複並核對。
  </Accordion>

  <Accordion title="重複">
    傳遞採用**至少一次**語意：在我們未收到回應後進行的重試，
    可能會重複傳遞事件。每次重試都帶有相同的
    `event_id`，因此請儲存已處理的 ID 並略過重複項目。`event_id`
    也會在端點之間共用——訂閱相同事件的兩個端點會收到相同的 `event_id`。
  </Accordion>

  <Accordion title="逾時">
    端點傳送每次嘗試的逾時時間為 **30 秒**。在
    舊版路徑中，會驅動即時通話行為的阻塞式請求——
    [`telephony.incoming` / `web.incoming`](/zh-Hant/webhooks/call-incoming)
    設定交換——電話通話的首次嘗試限制為 **10 秒**，
    包含其一次重試在內的整個交換限制為 **13 秒**，
    而小工具工作階段則限制為 **10 秒**。回應緩慢會延遲
    接聽通話，因此請盡量在幾秒內回應。Webhook 模式的[工具調度](/zh-Hant/tools/overview)
    預設允許 20 秒，且工具宣告可設定頂層 `timeout`。
  </Accordion>

  <Accordion title="來源 IP">
    傳出 webhook 來自 ThunderPhone 的雲端 IP 範圍。
    若你的防火牆需要允許清單，請聯絡支援團隊，我們將
    提供目前的範圍。
  </Accordion>
</AccordionGroup>

## 在舊版與端點式 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`](/zh-Hant/webhooks/call-incoming)） | 永不——僅傳送通知 |
| 最適合用於 | 動態通話設定 | 在正式環境中處理事件 |

新的整合應透過端點式 webhook 消費事件。只有在你需要於
接聽時動態設定通話，或使用 webhook 模式工具派送時，才應保留（或新增）
舊版 URL——這些請求／回應交換僅會在舊版路徑上執行。

---

## 相關資源

<CardGroup cols={2}>
  <Card title="事件目錄" icon="list" href="/zh-Hant/webhooks/events">
    所有事件類型及其酬載。
  </Card>
  <Card title="Webhook 端點" icon="bolt" href="/zh-Hant/webhooks/endpoints">
    管理多個端點、事件篩選條件與密鑰。
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/zh-Hant/webhooks/call-incoming">
    你的伺服器必須回應的封鎖式請求，用於設定通話。
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/zh-Hant/webhooks/call-complete">
    通話後酬載，包含逐字稿、錄音與指標。
  </Card>
</CardGroup>
