Using Xero Webhooks with Node: Express & Hapi examples
Over a million small businesses, and their advisors are looking for the best cloud apps that integrate with Xero. Partner with us, and we’ll make sure they find yours.
A developer emailed last week having some troubles using Xero’s webhooks implementation with Node and the Express JS framework.
By default both Hapi & Express parse incoming request bodies to make them easier to work with. Usually, that’s a welcome feature, except when responding to Xero Webhooks ‘Intent to Receive’.

For a more general overview of the Xero’s webhooks (ITR, request signing), see my previous post: https://devblog.xero.com/getting-hooked-on-xero-with-aws-lambda-9ed4075c5c23
Intent to Receive is also documented here. To summarise, when setting up Xero Webhooks, your webhook endpoint receives 4 requests. 3 of these requests are incorrectly signed, the 4th is correctly signed.
Your server must respond to the 3 incorrect ones with a HTTP 401, and the correct with a HTTP 200.
For each request, your server signs the request body received from Xero with your Xero Webhook Key, using HMAC SHA256, Base64 encrypted. I’ve used Node’s Crypto library in the examples below.
To get ahead of the curve, here is the code samples for getting Xero’s webhooks working with the Express & Hapi frameworks.
Using ngrok for testing locally:
This little beauty is makes it super easy to test webhooks locally. Download it here: https://ngrok.com/
It’s super easy to use:
./ngrok http 3000
It will then provide you an endpoint on the internet, which routes to a local address (by default localhost:3000). Your ngrok endpoint will be something like this: https://b6ffb5e9.ngrok.io
ngrok also provides a web interface for viewing the requests / responses, and a replay feature. Awesome!!

Now you just need to head to the Xero developer portal and start sending webhook events to our ngrok endpoint.

Xero Webhooks for Express JS
With over 15 million downloads in the past month, Express is the most popular web framework for JS.
Express uses a middleware called body-parser to parse request / response body parsers.
To get the ITR to work, we need to ensure that body-parser is not modifying incoming request bodies. We do this by creating a body-parser that takes the raw response body and passes it to our route. Line 19 & 22 show how you can create a specific body-parser for raw requests and use it on the route you receive webhooks.
Note: In the first sample I built I set the body-parser to handle all routes (set at the app level). This worked in the sample, but would cause problems if you’re adding webhooks to an existing app, as you probably don’t want to start handling all your requests in a raw format.
The default response (res) has no body, so it’s just a matter of setting the HTTP code and sending the response. Shown on lines 31–39.
Check out the full repo here: https://github.com/rjaus/xero-webhooks-express-js
To test it out yourself, clone the repo above. Replace “XERO_WEBHOOK_KEY” with your own webhook key: https://developer.xero.com/myapps/
Then run:
git clone https://github.com/rjaus/xero-webhooks-express-js xero-webhookscd xero-webhooks
npm install express
npm install body-parser
node express.js
Then hit “Send ITR” from your App in the developer portal. You should see:

Xero Webhooks for Hapi JS
Hapi JS has just under 500k download in the past month, putting it far behind Express in terms popularity. Hapi appears to be more popular with the big end of town, with a number of large companies such as Walmart & Disney using it in production.
It’s important to note which version of Hapi you’re using, I’ve written this sample with the latest (v17, released Oct 23rd 2017). Lots of the samples / examples online are made with previous versions, which have changed significantly enough to rarely work.
Things to watch out for:
1: Ensure your body payload is not parsed by the Hapi framework. Remember, any change, no matter how insignificant, will result in a completely different signature.
route.options.payload.parse is the option you need. By default it’s set to true, we need it to be false. As you can see in the below gist, lines 17–19 are setting this option for our /webhook route.
2: Your response contain only a HTTP code, that means no body. Thankfully that’s quite easy in Hapi as well. Simply instantiate your response object without specifying a body string, and then set it’s HTTP response code.
Checkout lines 35–42 below as an example.
3: Ensure you’re always using UTF8 encoding when generating your signature. The HMAC update function in Node’s crypto module defaults to UTF8 encoding, but I set it explicitly anyway. Example on line 31.
And here’s the complete sample:
Here’s the repo you can pull down for the full example: https://github.com/rjaus/xero-webhooks-hapi-js
To get it running, clone it, replace “XERO_WEBHOOK_KEY” with your own key, then:
npm install hapi
node hapi.js
And that’s it
We’re always here to help, and always keen for feedback. If you’re struggling with the Xero API, webhooks or an SDK, reach out and we’ll take a look. Reach us on Twitter or shoot us an email.