Webhook 是我們串接各種雲端服務與自動化工作流的好幫手,它讓 OpenClaw 可以在外部事件發生的當下「秒級」啟動對應流程。不過,開發過 Webhook 的人一定懂一個痛點:「收到請求」絕對不等於「這個事件只會來一次」。
有時候是第三方供應商的伺服器沒收到回應而自動重送,有時候則是網路波動,導致同一個請求送達了兩次。如果我們的系統一收到 Payload(資料負載)就急著去建立工單、寄發信件或寫入資料庫,很快就會產生一堆重複執行的副作用。
為了讓 OpenClaw 穩定運作,在把事件交給 Agent 執行之前,我們必須在入口處建立穩固的閘門。以下分享實務上必做的 5 個資料檢查關卡。
本篇目錄
一、先驗證 Webhook 簽章,確認來源真偽
這是確認請求「真的是從官方發出來」的第一關。我們應該使用服務供應商指定的 HMAC 演算法,直接拿原始的 Request Body 來計算摘要,再用「常數時間比較 (Constant-time comparison)」的方法來比對簽章。
這裡有個常踩的雷:千萬不要先把 JSON 重新排序或是格式化之後才去驗證。因為只要多一個空白鍵或是欄位順序不一樣,算出來的結果就會完全不吻合。另外,用來驗證的密鑰(Secret)必須從環境變數中讀取,絕對不能明碼寫在程式碼或日誌裡。只要簽章對不上,就直接回覆 HTTP 401 拒絕,別讓 Agent 浪費算力去解析不明內容。
二、把「事件 ID」當作冪等鍵,阻擋重複執行
為了解決 Webhook 重複觸發的問題,系統必須具備「冪等性(Idempotency)」。做法是從 Payload 中抓出供應商保證不會重複的那組 event_id,並在系統執行任何外部寫入前,先去資料庫查詢這筆處理紀錄。
你可以在資料庫用 event_id 建立唯一索引:
- 第一次看到它: 先把狀態寫入
processing(處理中),等流程跑完再更新為processed(已完成)。 - 發現是
processed: 代表已經處理過了,直接回傳 HTTP 200 (已接受) 並結束流程。 - 發現是
processing: 代表上一個同樣的請求還在跑,這時可以設計短暫等待,或把它丟進重試佇列。
切記,盡量不要拿整個內容的雜湊值 (Hash) 來取代事件 ID。因為有時候兩個不同的合法事件,內容可能恰巧一模一樣;反而同一個事件的重送請求,可能因為帶有不同的時間戳記,導致 Hash 值改變而失去防護作用。

三、檢查時間戳與 Replay Window,防範重放攻擊
有了事件 ID 只能擋下我們「已經記錄過」的重複事件。但如果是有人惡意攔截舊的請求重新發送(也就是俗稱的重放攻擊 Replay Attack)呢?
在驗證完簽章後,請立刻解析 Payload 裡面的時間戳(Timestamp),並與伺服器目前的時間做比對。我們可以設定一個明確的允許時間差,也就是建立一個 Replay Window(例如 5 分鐘內)。如果時間誤差超過這個窗口,或是來自太遙遠的未來,請直接把請求拒絕。這個檢查特別適合放在系統的最外層,能有效避免過期無效的 Payload 消耗系統資源。
四、嚴格驗證 Payload Schema 與資料型別
不要只檢查 event_id 存在就急著放行。請固定允許的 event_type,並檢查必要欄位、字串長度、數值範圍以及巢狀物件的資料型別。遇到未知的關鍵欄位或異常巨大的 Body,應該果斷阻擋。
針對 Schema 的版本也要明確管控:只有系統支援的 Webhook 版本才交給 OpenClaw 處理,不支援的版本則送進隔離佇列(Dead-letter queue)等待人工檢視。如果驗證失敗,記得回傳可讀的錯誤代碼,並且不要把原始的完整 Payload 直接印在日誌裡,以免不小心洩漏敏感個資。
五、先判斷 Replay 狀態,最後再執行副作用
前面這四項檢查都過關後,最後一步的「順序」非常重要:
- 原始 Body 驗證簽章
- 解析時間戳,確認在 Replay Window 內
- 驗證 Schema 格式
- 利用
event_id取得冪等鎖,判斷 Replay 狀態 - 最後,才是呼叫 Agent 工具或寫入外部系統(執行副作用)
鎖的狀態更新,必須和主要的副作用動作有清楚的補償策略。萬一 Agent 執行到一半當機了,系統要能保留 retryable (可重試) 的狀態,讓下一次重試時知道哪些步驟已經做過。另外,千萬不要為了圖方便,把所有錯誤都用 HTTP 200 掩蓋過去,也不要在還沒確實拿到冪等鎖之前,就提早向第三方回報處理成功。

加上「重送測試」,確保檢查機制確實生效
程式邏輯寫完後,建議建立一個不含真實憑證的測試事件。試著連續送出相同的 event_id 兩次,接著再模擬錯誤簽章、過期時間戳、缺少必要欄位等情境。
理想的驗收條件是:只有第一次合法事件能成功啟動流程;第二次送出相同事件會在入口處直接被辨識為 Duplicate / Replay 並擋下;其餘異常請求則完全不會喚醒 Agent。這比單純只看「一次成功回應」,更能證明你的 API 串接具備強大的抗重送能力。
常見問題 FAQ
這時千萬不要自己拿 Payload 內容去 Hash 當成唯一值。如果供應商真的沒給,你應該在系統入口處建立針對該服務的「業務邏輯去重策略」(例如:訂單號碼 + 最新狀態),並標記這條路線的風險,必要時將疑似重複的事件轉交人工檢視。
實務上通常會回傳 HTTP 200 OK 或 202 Accepted,因為你的系統已經「知道並妥善處理過」了。回傳錯誤碼反而會導致供應商的系統誤判而瘋狂重試。不過,系統內部日誌必須明確標記這是一筆 Duplicate,方便日後追蹤。
不一定。「冪等鎖」、「唯一索引」與「外部副作用」之間的交易邊界依然要設計正確,務必要透過實際的重送測試來確保流程萬無一失。
