桌面事件轉送

# 讓其他系統收到 Watch 通知

在 Watch 介面設定接收網址，再用測試事件確認通知送達。一般使用者負責選擇要通知的系統；接收端程式與簽章密鑰由整合人員準備。

## 1\. 準備接收端

請接收系統的負責人提供 Watch 主機能連到的網址。雙方需使用相同簽章密鑰；請管理員在 Watch 的啟動環境設定密鑰，並提供對應的環境變數名稱，例如 SENTINEL\_CONNECTOR\_SECRET。不要把密鑰內容填入網址或貼到聊天。

## 2\. 在 Watch 建立事件轉送

1.  開啟「設定 → API 與整合」，展開「進階：將事件轉送到其他系統」。
2.  填入接收系統名稱、接收網址及密鑰的環境變數名稱。確認建立後顯示「已簽章」，而不是「缺少 secret」。
3.  第一次測試選 critical，事件種類勾選 sdk.test。正式即時告警使用 live.event；需要離線影片事件時再選 offline.event。
4.  接收端準備好後，取消「先模擬測試，不實際傳送」，再按「建立事件轉送」。保留勾選只會模擬，不會發出 HTTP 通知。

## 3\. 確認真的收到測試通知

在「測試整條路徑」按 critical。顯示測試事件已加入佇列，只代表 Watch 已受理；請接收端確認簽章驗證成功且事件已保存，並核對 Watch 的傳送紀錄。若沒收到，先檢查 sdk.test 是否勾選、是否仍為 dry-run、密鑰是否可用，以及接收網址是否可連線。

測試成功後，由現場人員確認一次實際告警也能送達。測試通知只驗證傳送，不代表攝影機、任務條件或分析結果已驗收。已有 dry-run 項目時，可另建實際傳送用的項目，避免把模擬成功當成送達。

## 接收端格式：sentinel.alert.v1

v0.39 桌面介面建立的事件轉送預設使用以下格式，Content-Type 為 application/cloudevents+json。欄位直接位於最外層，沒有 data 包裝。以下只列常用欄位，值為示意。

```
{
  "schema": "sentinel.alert.v1",
  "contract_version": 2,
  "specversion": "1.0",
  "id": "evt_example",
  "type": "sdk.test",
  "time": "2026-08-29T23:41:08Z",
  "state": "raised",
  "severity": "critical",
  "title": "Integration test",
  "summary": "Test event from Watch",
  "camera_id": null,
  "snapshot_url": null,
  "correlation_id": "evt_example"
}
```

桌面轉送的 sentinel.alert.v1 與 GET /v1/events 的 sentinel.event.v1 格式不同。接收器須驗證正確的 schema；不要直接套用公開事件查詢的欄位。既有 Agent Alert Webhook 通道的 schema: 2 也不是本頁的桌面事件轉送格式。

## 驗證簽章

```
X-Sentinel-Delivery-ID: whd_example
X-Sentinel-Event-ID: evt_example
X-Sentinel-Timestamp: 1788056468
X-Sentinel-Signature: v1=<hex HMAC-SHA256>
Idempotency-Key: whd_example

signed_bytes = utf8(timestamp) + b"." + raw_request_body
```

```
import crypto from "node:crypto";

export function verifyWatch(secret, timestamp, rawBody, signature) {
  if (!secret || typeof timestamp !== "string" ||
      typeof signature !== "string" || !Buffer.isBuffer(rawBody)) return false;
  const seconds = Number(timestamp);
  if (!Number.isInteger(seconds) || Math.abs(Date.now() / 1000 - seconds) > 300) return false;
  const expected = "v1=" + crypto.createHmac("sha256", secret)
    .update(timestamp + ".").update(rawBody).digest("hex");
  const actualBytes = Buffer.from(signature);
  const expectedBytes = Buffer.from(expected);
  return actualBytes.length === expectedBytes.length &&
    crypto.timingSafeEqual(actualBytes, expectedBytes);
}
```

接收器先保留原始請求內容，檢查時間戳與簽章，通過後才解析 JSON。確認 schema 為 sentinel.alert.v1、specversion 為 1.0，並檢查 X-Sentinel-Event-ID 等於內容的 id，Idempotency-Key 等於 X-Sentinel-Delivery-ID。桌面只有在密鑰可讀取時才附簽章，接收器應拒絕未簽章的請求。

## 重送與圖片

通知可能重送。先以傳送 ID 去重並保存事件，再盡快回覆 2xx；你的業務流程也應避免重複處理同一事件。Watch 的重試與傳送紀錄在桌面介面查看，公開唯讀 API 不提供重送管理。

通知中的 snapshot\_url 屬於桌面相容介面，不應直接拿到 LAN 入口使用。若接收端需要圖片，使用 events:read 金鑰查詢同一 id 的 GET /v1/events/{event\_id}，再依 evidence.image\_url 下載可用截圖。

[讀取事件與截圖](https://watch.tensordynamics.org/developers/events?lang=zh)

---
Source: https://watch.tensordynamics.org/developers/webhooks?lang=zh
More Watch guides: https://watch.tensordynamics.org/llms.txt
