Xero - beautiful accounting software

Xero Developer Help Center

Xero Developer Community

Community > SDKs >

Webhook Signature Validation in PHP

Started by Mark Portbury -   in SDKs


I am trying to validate a webhook sent for my private app in PHP. I have followed the guidance in this stack overflow question, but I cannot get the Intent to Receive to work.

I am using the code as-is from the stack overflow thread. I have declared the $yourWebhookKey variable using the webhook string in my private app webhook portal, which is 88 characters long.

My $hash value (hash of payload and webhook key using HMACSHA256, then base64 encoded), is 88 characters long. However, the Xero Signature I receive is only 44 characters long. Do I need to process the Xero signature in any way before comparing it to the $hash?
Hey Mark,

During the Intent to Receive phase of setting up your webhooks, we will send a series of events to your subscription URL. Some of these will be correctly signed, whilst others will not be correctly signed.

Is it possible that the event that is causing issues is one of the incorrectly signed events? If so, your system should return a 401 response back to our server.


Matthew Mortimer (Xero Staff)  

Thanks for the speedy response Matt.

I currently have an equivalence check between the hashed payload and the signature received from Xero. If there's not a match, I'm throwing a 401 header.

To help me see what's going on, I am sending an email from the code to myself of the value of the hash and the value of the signature. The hash is consistently 88 characters long, and the signature is consistently 44 characters long.

The error on the webhooks setup page after sending the intent to receive is "Response not 200".

Mark Portbury  

Hi mark,
I have been reading through your posts above.
am I able to clarify a few things to help pint point the issue and help solve this for you?

when you say you are base64 encoding the payload and key are you doing this together?

Are you using the hash_hmac function in conjunction with the base64_encode function?

I believe if you set the raw output to true that this will compute at 44 characters rather than the 88 you are seeing.


Vanessa Thornton (Xero Staff)  

Hi Vanessa

Unfortunately the forum isn't allowing me to add my code block in, otherwise I could copy paste it in here.

I have copied the code line for hashing from the stack overflow question. I am using the hash_hmac function to hash the payload and my webhook key, using sha256. I then base64 encode the hash.

I am then comparing the hashed and encoded payload (88 characters) with the signature received from xero (HTTP_X_XERO_SIGNATURE), which is 44 characters.

Mark Portbury  

Hi Mark,
I have been able to successfully use this function in PHP and get 44 characters when setting the bool flag of raw_output to true. When i use it's default it produces 88 characters.

Have you tried setting this flag to true?

Vanessa Thornton (Xero Staff)  

Yep that got it, thanks Vanessa! I appreciate your help. Excited to get the webhooks running!

Mark Portbury  

awesome! great news. We are also excited to have you on board and using our webhooks system.

All the best,

Vanessa Thornton (Xero Staff)  


I have been trying to validate my webhook with ITR but the signatrure in the header always differes by one character. I do not know if this is some issue related to the fact that I am testing it on my local machine and using ngrok app to tunnel the request. The PHP version is 7.1.14.

This code is provided by the developer community - Xero does not warrant it in any way

Do you happen to know why this could be different?

Best wishes,

Adrian Drozdz  

+1 facing the same issue as Adrian did.

Wayne Wen  

@Adrian I resolved it by returning '' for status 401 as well. Make sure neither 401 or 200 is returning content in body.

Wayne Wen  

+1 facing the same issue as Adrian

For some reason the signature comes out slightly different:

This code is provided by the developer community - Xero does not warrant it in any way

Brett O'Donnell  

My PHP code is:
I have copied code form here

if ($computedSignatureKey == $xeroSignatureKey) {
} else {
I have return 200 from code.

but it return webhook page "Response to incorrectly signed payload not 401"

Can you suggest me what is wrong here.

ajay shukla