Twilio authenticates webhooks by calculating a signature based on the webhook url and parameters. The parameters are sorted and all spaces (%20) are replaced with a plus sign.
For example, if one issues url and parameters as follows
https://example.com/voiceCallback?name=Jane%20Doe&assoc=123&id=abc
The callback function will receive an event with the following property:
"queryStringParameters": { "assoc": "123", "id":"abc", "name": "Jane Doe" },
There’s no way for the callback function to determine the original parameter order. As a result, in order to calculate the right signature to validate this as a Twilio request in the callback function, the point of issue must have the parameters in alphabetical order and replace all %20 with +(plus) symbols. Also, use &, not & or %26 as the parameter separator.
See this link on how to construct a Twilio webhook url.
Here’s a quick glance:
const url = req.protocol + '://' + req.get('host') + req.originalUrl; // Sort the params const sortedParams = qs .stringify(params, { arrayFormat: 'brackets' }) .split('&') .sort(sortByPropertyOnly) .join('&') .replace(/%20/g, '+');