Twilio enables software developers to programmatically make and receive phone calls, send and receive text messages, and perform other communication functions using its web service APIs.
Axway’s API Builder and Twilio can be used to integrate SMS into your integrations, applications, and systems.
In this blog post, we’ll describe one way to design an API Builder API to serve as an SMS Webhook so you can take action on incoming SMS.
In this example, we’ll use the Custom API feature of API Builder and the Twilio npm.
Twilio SMS Webhook
We’ll implement Twilio SMS Webhook “Type 1: Incoming Message” which leverages a webhook associated with your Twilio SMS phone number.
The Webhook will be an API Builder custom API that will respond to the Webhook POST. In order to set up the Twilio Webhook URL, you will need an internet-accessible API Builder API. My API Builder API is called webhook.
During development, I used ngrok to make my API internet accessible. Once your API Builder project is done and deployed, you can replace the Twilio Webhook URL with the URL of your deployed API Builder API.
To set up the Twilio Webhook URL go to the Twilio Console and click on “All Products and Services” and then on “Phone Numbers”:
Click on the desired phone number, and scroll down to messaging and enter the API Builder API URL:
API Builder Custom API
Here is what the API Builder API should do when it is called from Twilio:
- Validate that the request is from Twilio as described here
- This is optional but important since we cannot enable authentication in the API Builder API since Twilio does not support setting headers in its Webhook setup.
- Optionally send a response to the Twilio Webhook call with a message to the SMS sender
- Process the incoming SMS
A sample Custom API is shown below:
var APIBuilder = require('@axway/api-builder-runtime');
const validateTwilioWebhook = true;
const apiPath = '/api/webhook';
const twilioClient = require('twilio');
const MessagingResponse = require('twilio').twiml.MessagingResponse;
var webhook = APIBuilder.API.extend({
group: 'Webhooks',
path: apiPath,
method: 'POST',
description: 'Twilio SMS Webhook',
parameters: {
ToCountry: { description: 'ToCountry', type: 'body', require: false },
ToState: { description: 'ToState', type: 'body', require: false },
SmsMessageSid: { description: 'SmsMessageSid', type: 'body', require: false },
NumMedia: { description: 'NumMedia', type: 'body', require: false },
ToCity: { description: 'ToCity', type: 'body', require: false },
FromZip: { description: 'FromZip', type: 'body', require: false },
SmsSid: { description: 'SmsSid', type: 'body', require: false },
FromState: { description: 'FromState', type: 'body', require: false },
SmsStatus: { description: 'SmsStatus', type: 'body', require: false },
FromCity: { description: 'FromCity', type: 'body', require: false },
Body: { description: 'Body', type: 'body', require: false },
FromCountry: { description: 'FromCountry', type: 'body', require: false },
To: { description: 'To', type: 'body', require: false },
ToZip: { description: 'ToZip', type: 'body', require: false },
NumSegments: { description: 'NumSegments', type: 'body', require: false },
MessageSid: { description: 'MessageSid', type: 'body', require: false },
AccountSid: { description: 'AccountSid', type: 'body', require: false },
From: { description: 'From', type: 'body', require: false },
ApiVersion: { description: 'ApiVersion', type: 'body', require: false }
},
action: function (req, resp, next) {
// Validate request is from Twilio
if(validateTwilioWebhook) {
let webhookURL = 'https://'+req.headers.host+apiPath;
if(!twilioClient.validateRequest(process.env.TWILIO_AUTHTOKEN, req.headers['x-twilio-signature'], webhookURL, req.body)) {
console.log('Invalid Twilio Signature!!!');
resp.response.status(500);
next();
return;
}
}
// Respond to Twilio Webhook Request
const twiml = new MessagingResponse();
twiml.message('Hang tight, we are working on your request ...');
resp.setHeader('Content-Type', 'text/xml');
resp.response.status(200);
resp.response.send(twiml.toString())
next();
// Process SMS Message
}
});
module.exports = webhook;
The API code requires in the twilio npm so we can leverage the Twilio validateRequest() method to validate the incoming Webhook call, as well as the MessagingResponse() constructor to create the response to the incoming webhook.
Then we scaffold the custom API by setting the group, path, method, description and parameters.
Finally, we implement the API function in the action property.
Webhook Validation
The validation code is wrapped in an if() statement so we can also debug our API using Postman instead of sending actual SMS messages. In order to truly test and debug this validation code, you need to actually send a real SMS. However, once it’s working, you can disable it temporarily while you develop the rest of your API and trigger the API from Postman. The parameters we pass to the Twilio validateRequest() method:
- process.env.TWILIO_AUTHTOKEN – this is your Twilio Auth Token environment variable (e.g. stored in the API Builder project /conf/.env file)
- req.headers[‘x-twilio-signature’] – this is the header that Twilio sends with the Webhook call
- webhookURL – this is the URL of the Webhook entered into Twilio. It is the URL of this API
- req.body – this is the webhook body
Webhook Call Response
If you want Twilio to respond to the SMS immediately, then you can send the response as TWIML XML. The MessagingResponse() method helps construct this as shown in the code sample above.
At this point, you can do whatever you need to do with the SMS message.
Summary
In this blog post, we saw how easy it is to create an API Builder Custom API that acts as a Webhook for Twilio incoming SMS.
Discover more about tips on using Stoplight for API Design for API Builder.