Menu

Rate this page:

Thanks for rating this page!

We are always striving to improve our documentation quality, and your feedback is valuable to us. How could this documentation serve you better?

Receive SMS Messages with A LinkIt ONE using Programmable Wireless, AWS IoT and Lambda

Want to receive - and react to - SMS and MMS messages on a LinkIt ONE? We'll help you do just that today, using Twilio's Programmable Wireless and Amazon Web Services' IoT, API Gateway, and Lambda. Upon receiving an incoming SMS or MMS, a Twilio number will trigger a Webhook that sets our infrastructure in motion.

If you haven't done so yet, run through our guide on sending SMS or MMSes with the LinkIt ONE and Twilio Programmable Wireless. We'll be building on top of the same infrastructure in this post, and will assume you have it in place. As before, this code will work with either Programmable Wireless or WiFi.

Already ran through the last guide? Awesome, let's let the LinkIt talk back!

What is a Webhook?

Webhooks are user-defined HTTP callbacks. They are usually triggered by some event, such as receiving an SMS message or an incoming phone call. When that event occurs, Twilio makes an HTTP request (usually a POST or a GET) to the URL configured for the webhook.

To handle a webhook, you only need to build a small web application that can accept the HTTP requests. Almost all server-side programming languages offer some framework for you to do this. Examples across languages include ASP.NET MVC for C#, Servlets and Spark for Java, Express for Node.js, Django and Flask for Python, and Rails and Sinatra for Ruby. PHP has its own web app framework built in, although frameworks like Laravel, Symfony and Yii are also popular.

Whichever framework and language you choose, webhooks function the same for every Twilio application. They will make an HTTP request to a URI that you provide to Twilio. Your application performs whatever logic you feel necessary - read/write from a database, integrate with another API or perform some computation - then replies to Twilio with a TwiML response with the instructions you want Twilio to perform.

Building Infrastructure to Receive Incoming Messages

We're going to turn the architecture from the sending article on its head for this objective. Now, when Twilio receives and incoming message, it will post to an endpoint you expose with API Gateway. API Gateway will trigger Lambda, which we will give permission to post directly to the twilio topic on MQTT.

LinkIt Receive Messages Infrastructure

Remember, the LinkIt ONE is already subscribed to the MQTT twilio topic; we simply need to add some business logic which reacts to incoming messages. We can then respond in the same way we did in the sending guide - by using the Lambda send message function.

Mock an API Endpoint with AWS API Gateway

We’re going to use Amazon’s API Gateway to expose the endpoint Twilio will hit when it sees incoming messages.

There are quite a few steps here, so we've condensed them for you to take it a step at a time:

  • Go to the API Gateway console
  • Create a new API in the same AWS region as IoT and the first Lambda function from the previous post
  • Name it ‘Twilio-to-IoT’ and enter a description
  • You’ll now be redirected to your new API’s ‘Resources’
  • In the ‘Actions’ menu, select ‘Create Resource’
  • Name it ‘Messages’ and check the ‘Resource Path’ is ‘/messages’:

AWS New Child Resource

  • Create ‘/messages’ by hitting the blue 'Create Resources' button
  • In the ‘Actions’ menu, select ‘Create Method’, then ‘POST
  • Click the tiny checkbox next to POST. In the ‘Integration Type’ select ‘Mock’
  • You’ll then be redirected to a screen that looks like this:

API Gateway Method Execution

  • In the ‘Actions’ menu, select ‘Deploy API’
  • Create a new stage and name it ‘prod’ (and any description)
  • Deploy your mock API Gateway to prod
  • Next, you’ll be redirected to the prod stage editor (if not, it’s under your API in ‘Stages’)
  • Click 'POST' under messages
  • Find the ‘Invoke URL’ at the top of the page and copy it somewhere safe; that is the endpoint we will eventually use with Twilio's webhook

Invoke URL API Gateway

Great stuff! You've not got a mock endpoint set-up. We'll eventually return here to add the logic we need to receive and reply to Twilio, but for now, you're done. Next, we're going to move on to Lambda.

Create a New Amazon Lambda Function

In the sending messages article, we created a simple function which POSTed to Twilio after receiving carefully constructed MQTT messages. For receiving messages, we're going to do roughly the opposite, and forward incoming messages back to the LinkIt over the same channel.

Again, this is quite a few steps, so we've condensed them to make them easier to follow.

Here's a look at receiving messages:

        
        
        
        
        Receive a text message with AWS Lambda then pass along the data to a MQTT topic.

        Receive a Text Message with AWS Lambda

        Receive a text message with AWS Lambda then pass along the data to a MQTT topic.

        As a message comes in from Twilio, we quickly do some checks for validity, message size, and the like. On an embedded platform you'll want to do a lot of this on the server.

        Of special note, you can see we're checking for the existence of the X-Twilio-Signature header. This signature is a hash of the POSTed form plus your API Secret, which you can find in the Twilio console. See more about how Twilio helps you secure incoming requests.

        Let's continue the build!

        • Change ‘Code entry type’ to ‘Upload a .ZIP file’ and upload the ZIP file
        • Set the following FOUR environment variables:
          AUTH_TOKEN (from the Twilio Console)
          AWS_TOPIC (set this to twilio, no quotes)
          REQUEST_URL (use the Invoke URL from API Gateway we called out above)
          AWS_IOT_REGION (use the region your LinkIt IoT resides in, e.g.: us-east-1 or us-west-2)
        • Set ‘Handler’ to ‘twilio_functions.twilio_webhook_handler’
        • For ‘Role’, create a new role and use the IoT Button template (remember what you name this role, you'll need it later)

        For now, that's all you'll need to do on the Lambda side. However, we're not quite finished with this specific functionality. You need to give a user some new permissions.

        As you can see in the code, at one point we do this:

            aws_region = os.environ['AWS_IOT_REGION']
            aws_topic = os.environ['AWS_TOPIC']
            client = boto3.client('iot-data', region_name=aws_region)
        
            client.publish(
                topic=aws_topic,
                qos=0,
                payload=json.dumps({
                  "To": form_parameters[u'To'],
                  "From": form_parameters[u'From'],
                  "Body": form_parameters[u'Body'],
                  "Type": "Incoming"
                })
            )
        

        We're using the boto3 package and trying to publish directly to the twilio MQTT topic in whichever region you set in the environmental variables.

        However, if you ran this now... you'd have no luck. Let's fix that.

        Grant Your Code More Permissions with IAM

        Go to IAM by using the 'My Security Credentials' in your name pulldown (top of the screen):

        IAM Credentials

        • Click on the 'Roles' link on the left
        • Find the role you made for the Lambda function and click on it to make it active
        • Click on the 'Create Role Policy' button under 'Inline Policies':

        Inline Policy Creation

        • Pick ‘Custom Policy’
        • Here’s the policy to use:
        {
           "Version": "2012-10-17",
           "Statement": [
               {
                   "Effect": "Allow",
                   "Action": [
                       "iot:Publish"
                   ],
                   "Resource": [
                       "*"
                   ]
               }
           ]
        }
        
        • Name it with your choice of name (you won't come back to it) and save it

        And with that, Lambda can now publish to the twilio MQTT topic. Let's return now to API Gateway and build out our mock API.

        Fully Build Out Your API in API Gateway

        By default, API Gateway is made to handle JSON data. Twilio expects, however, to be able to POST form data and to receive XML in the form of TwiML. These next steps will both ensure our Lambda function is called, as well as change the API Gateway defaults to handle form POSTs as well as XML.

        Ready to dive in? We've carved up the steps for you to follow once again. Start by heading back to the API Gateway Console.

        • After logging in (if necessary), click 'Resources' under your API:

        API Gateway Resources

        • Click the ‘ANY’, in purple, which was automatically added by Lambda with the name of the function
        • In the ‘Actions’ menu, choose ‘Delete Resource’
        • You’re now in the same place as the original Mocked API

        Add a Lambda Integration (Integration Request)

        At this point, we need to add back the Lambda integration.

        • Click ‘Integration Request’ and change the ‘Integration Type’ to Lambda function
        • Select your region and function
        • Click 'Save'
        • Click through the 'overwriting' prompt
        • Next, click the right arrow to expand the ‘Body Mapping Templates’
        • Delete any body mappings that currently exist (e.g.: application/json)
        • Add a new body mapping for: application/x-www-form-urlencoded
        • Add this as the body mapping template:
        #set($httpPost = $input.path('$').split("&"))
        {
        "twilioSignature": "$input.params('X-Twilio-Signature')",
        #foreach( $kvPair in $httpPost )
        #set($kvTokenised = $kvPair.split("="))
        #if( $kvTokenised.size() > 1 )
          "$kvTokenised[0]" : "$kvTokenised[1]"#if( $foreach.hasNext ),#end
        #else
          "$kvTokenised[0]" : ""#if( $foreach.hasNext ),#end
        #end
        #end
        }
        

        Here we’re taking every POSTed parameter and mapping it to JSON to pass to Lambda. In Lambda, you'll recall, we use the POSTed parameters to validate requests are from Twilio.

        • ‘Save’ the template
        • Click the back arrow ‘Method Execution’ at the top

        Pass Through the X-Twilio-Signature Header (Method Request)

        To validate incoming requests, we need to pass along the X-Twilio-Signature header as discussed above. Let's add that now.

        • Click ‘Method Request’
        • Expand ‘HTTP Request Headers’
        • ‘Add header’ and add the header ‘X-Twilio-Signature’
        • Save that and return to the main screen with the ‘Method Execution’ link

        Add XML Support to Your API (Integration Response)

        When Twilio hits your endpoint, it expects XML in return. Now, let's modify the API to return that XML.

        • Click ‘Integration Response’.
        • Expand the right arrow by clicking on it
        • Delete anything that exists in the ‘Body Mapping Templates’ area
        • Click ‘Add mapping template’ and add ‘application/xml’.
        • For the template itself, use:
        #set($inputRoot = $input.path('$'))
        $inputRoot
        

        We are simply passing along all data with this template, echoing inputRoot into the body. This lets us write XML directly into the body without API Gateway attempting to rewrite anything.

        • Click the ‘Method execution’ link to return to the main API screen

        Continue XML Support (Method Response)

        There is one last step for our XML. We need to return application/xml when we give the all clear 200.

        • Click ‘Method Response’
        • Modify the ‘Response Body' for 200 to be ‘application/xml’.
        • Go back to ‘Method execution’ by clicking the link at top

        Almost done now!

        Deploy Your API

        There is just a single API Gateway objective left - deploying!

        • In ‘Actions’, scroll down to ‘Deploy API’
        • Deploy it to prod
        • Copy the ‘Invoke URL’ to your clipboard

        Make sure that you have copied the URL to the clipboard. The last step is with Twilio itself; we're going to paste that URL into the Webhook and test the integration!

        Set Up Your Twilio Webhook

        The last step is to change the behavior of one of your Twilio phone numbers in the console. We will set the message webhook to the URL you copied in the above step.

        • Go to the Twilio Console now
        • Click the ‘#’ icon to see the numbers you own
        • Pick - or buy - a number which handles MMSes and SMSes (or just SMSes, depending on what you plan to send)
        • Paste the execution Invoke URL from API Gateway in the ‘Messaging’ Webhook:

        SMS Webhook

        And that's it! Power cycle your LinkIt and wait for it to connect to AWS IoT.

        Then text it something like 'Racecar'.

        C'mon, you know you want to...

              
              
              
              
              We consume an incoming text message, reverse the body text, and return it to the sender over MQTT.

              Receive a Text Message on the LinkIt ONE

              We consume an incoming text message, reverse the body text, and return it to the sender over MQTT.

              Building Out the Internet of Things with Twilio and a LinkIt ONE

              With this guide and the guide on sending messages with Twilio's Programmable Wireless, AWS, and the LinkIt ONE, you've now got both inbound and outbound messaging working with your board.

              Paul Kamp Kat King
              Rate this page:

              Need some help?

              We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.