Xero: como funciona a entrega de webhooks

A Xero exige 200 em até 5 segundos, sem corpo, e um 401 para assinatura inválida antes de ativar um webhook. O formato verificado e um verificador Node.

Comportamento de entrega

Lido na documentação do próprio provedor, verificado pela última vez em 2026-08-06. Onde o provedor não informa um número, esta tabela também não traz nenhum.

Prazo de resposta5 s

200 em até 5 segundos, sem corpo e sem cookies; 401 quando a assinatura é inválida

Verifique se seu handler responde a tempo

Novas tentativasnão publicado pelo provedor
Desistenão publicado pelo provedor
Fontehttps://developer.xero.com/documentation/guides/webhooks/configuring-your-server/

Assinatura

Verificada com um verificador funcional, comprovado por uma suíte de testes, e não lida na documentação. Última verificação em 2026-08-20.

AlgoritmoHMAC-SHA256
Payload assinadoraw body
Codificaçãobase64
Cabeçalhox-xero-signature = base64
Tolerâncianenhuma exigida

Verifique a assinatura

O corpo bruto da requisição, byte a byte, antes de qualquer parsing de JSON. Todos os esquemas desta página quebram no momento em que um framework serializa o payload de novo.

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.

Armadilhas

O webhook fica desligado até passar no Intent to Receive: a Xero envia uma entrega assinada corretamente e outra com assinatura propositalmente errada, e espera 200 para a primeira e 401 para a segunda. Um endpoint que responde 200 para tudo nunca entra no ar. Com a origem Xero e a chave de webhooks configuradas, o AnyHook responde às duas por conta própria e não armazena nenhuma.

Uma entrega é um lote de ponteiros, não de registros: cada evento indica uma categoria, um tipo e uma resourceUrl para buscar com uma chamada autenticada. O AnyHook dá ao evento o nome do primeiro do lote (invoice.update), para que os filtros e o log de eventos usem o mesmo nome.

Leia também

O AnyHook fica entre Xero e seus endpoints: responde em cerca de um segundo, tenta de novo segundo um cronograma próprio quando seu servidor está fora do ar e guarda cada evento disponível para replay. Troque uma URL e mantenha seu código.

Como funciona →