Xero 如何傳遞 Webhook

Xero 要求 5 秒內回 200 且不帶主體,簽章錯誤時回 401,之後才會啟用 Webhook。附經驗證的格式與 Node 驗證程式。

傳遞行為

取自服務商自己的文件,最後檢查於 2026-08-06。服務商沒寫數字的地方,這張表也不填。

回應時限5 秒

5 秒內回 200,不帶回應主體、不帶 Cookie;簽章錯誤時回 401

檢查你的處理程式能否及時回應

重試服務商未公開
放棄服務商未公開
出處https://developer.xero.com/documentation/guides/webhooks/configuring-your-server/

簽章

用一個經測試套件證明可運作的驗證程式核對過,不是照文件抄來的。最後驗證於 2026-08-20。

演算法HMAC-SHA256
簽章內容raw body
編碼base64
標頭x-xero-signature = base64
容許時間差不強制

驗證簽章

要用原始請求主體(raw body),逐位元組原封不動,並在任何 JSON 解析之前取得。框架只要重新序列化 payload,這頁上的每一種機制就會立刻失效。

const crypto = require("node:crypto");

// base64(HMAC-SHA256(webhooks key, raw body))
const expected = crypto
  .createHmac("sha256", webhooksKey) // developer.xero.com → My Apps → Webhooks
  .update(rawBody)
  .digest("base64");
const received = Buffer.from(req.headers["x-xero-signature"] ?? "");
const wanted = Buffer.from(expected);
// timingSafeEqual throws on a length mismatch, so check that first.
const valid =
  received.length === wanted.length && crypto.timingSafeEqual(received, wanted);
// Intent to Receive needs both answers: 200 (empty body) when valid, 401 when not.

容易踩的坑

通過 Intent to Receive 之前,Webhook 一直是關閉的:Xero 會送一次簽章正確的傳遞和一次故意簽錯的傳遞,要求第一次回 200、第二次回 401。對什麼都回 200 的端點永遠不會啟用。設定好 Xero 來源與 Webhooks 金鑰後,AnyHook 會自己回應這兩次,而且兩次都不儲存。

一次傳遞是一批指標,不是資料本身:每個事件標出一個類別、一個類型,以及一個要用經過驗證的呼叫去取得的 resourceUrl。AnyHook 以批次中的第一個事件為這次傳遞命名(invoice.update),所以篩選和事件紀錄看到的名稱都一致。

延伸閱讀

AnyHook 架在接收 Xero Webhook 的端點前面:大約 1 秒就回應,伺服器當機時依自己的排程重試,並讓每個事件都能重播。 換一個網址,程式碼不用動。

運作方式 →