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
时间容差无强制要求

验证签名

要用原始请求体,逐字节一致,并且在任何 JSON 解析之前。框架一旦重新序列化请求体,本页的每一种方案都会失效。

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 key 后,AnyHook 会自己应答这两个请求,并且都不保存。

一次投递是一批指针,而不是记录:每个事件只写明类别、类型和一个 resourceUrl,需要用带认证的请求去拉取。AnyHook 以批次中的第一个事件命名(invoice.update),这样过滤器和事件日志看到的名字是一致的。

延伸阅读

AnyHook 挡在接收 Xero Webhook 的端点前面:约 1 秒应答,你的服务器宕机时按自己的计划重试,并且每个事件都可以重放。 只改一个 URL,代码不用动。

工作原理 →