In this tutorial, we show you how to run some code on the cloud to send a text message (SMS) to a user when they submit an online form.

About Webhooks

A webhook is a piece of computing code that runs outside of FormSmarts whenever a form is submitted.

FormSmarts triggers the code execution by making an HTTP request to a URL you have configured. The code receives form data with the HTTP request so it can do something useful with it: in this example, we're going to send an SMS to the cellphone number provided on the form.

When you use a webhook to integrate FormSmarts with your website, the code runs on the server hosting your site. In contrast, here we create a serverless app that runs on the cloud, so you can benefit from this tutorial even if you don't have a website or your host doesn't allow you to run scripts on the server.

Prerequisites

You will need both a FormSmarts and an AWS account.

This tutorial requires little coding skills (the Python code provided should run as is) and the AWS services needed for this tutorial are configured online via the AWS Console. If you think you would benefit from integrating your forms with a webhook but don't have the technical skills, please contact us for advice.

The Online Form

We've made a simple form to support this tutorial. Feel free to try it, you should get an SMS confirmation (restricted to US numbers to control cost).

If you want to create a working webhook, replicate this form in your own FormSmarts account. Make sure the form has at least two input fields, the first one for the user's name, the second one for their cellphone number.

The Python Code

import boto3
import os
import json
import logging
import webhook_authenticator

SMS_TEMPLATE = 'Hello %s, this is a confirmation of your form submission #%s on %s.'


logger = logging.getLogger()
logger.setLevel(logging.INFO)


def send_sms(phone_number, message):
    """
    :param phone_number: phone number in E.164 format
    :param message: SMS message text
    """
    client = boto3.client('sns')
    client.publish(PhoneNumber=phone_number, Message=message)


def format_number(number, country_code=1):
    """
    Format phone number in E.164 international number format. https://en.wikipedia.org/wiki/E.164
    :param number: phone number
    :param country_code: country dialing code, defaults to 1 (US, Canada,...)
    """
    num = [c for c in number if c.isdigit()]
    num.insert(0, '+%s' % country_code)
    return ''.join(num)


def lambda_handler(event, context):
    """
    The code executed each time your form is submitted
    :param event: the request info passed to Lambda, including the webhook notification payload
    :param context: not used
    """
    # Reconstruct webhook URL so it doesn't have to be configured as an environment variable
    url = '%s://%s%s' % (
        event['headers']['X-Forwarded-Proto'],
        event['multiValueHeaders']['Host'][0],
        event['requestContext']['path']
    )

    # Verify request is from FormSmarts
    auth = webhook_authenticator.Authenticator(os.environ['FORMSMARTS_WEBHOOK_KEY'], url, event['body'])
    if auth.verify_request(event['headers']['Authorization']):
        notif = json.loads(event['body'])
        message = SMS_TEMPLATE % (
            # Load FormSmarts webhook notification JSON payload
            notif['fields'][0]['field_value'].title(),  # The person's name is the value of the first field
            notif['fs_ref_num'],  # FormSmarts reference number of the form entry
            notif['api_date']  # UTC time and date the form was submitted
        )
        # Phone number is the value of the second field
        number = format_number(notif['fields'][1]['field_value'])
        send_sms(number, message)
        logger.info('Confirmed form entry #%s to %s.' % (notif['fs_ref_num'], number))
        return {'statusCode': 200}  # OK
    else:
        return {'statusCode': 401}  # Authentication failed

The send_sms() function uses the AWS Simple Notification Service (SNS) to send the SMS mesage.

The format_number() function converts the phone number to the format required by SNS.

The lambda_handler() function is executed whenever the form is submitted. It verifies that the request was sent by FormSmarts, then gets the user's first name, which is the value of the first field of the form: notif['fields'][0]['field_value'] and the phone number, which is the value of the second input field: notif['fields'][1]['field_value']. The SMS message is then sent by calling send_sms(number, message) and the event is logged.

Downloads

The Lambda deployment package for this tutorial includes both the code above and the FormSmarts Webhook Authenticator for Python.

Deployment on AWS

We're not quite done yet. We still need to create and configure the cloud resources that will run the code.

Deploy the Code to Lambda

Lambda is a service that runs code when an event occurs. To create a Lambda function, log in to the AWS Console and click Create Function. Choose the Author from scratch option.

  • Set the name as FormSmartsWebhookListener
  • Select the Python 3.x runtime
  • Create a new Role form template(s)
  • Set the Role name to LambdaWithSns
  • Hit the Create function button.

Create a Function

Scroll down to the Create a function section and select Upload a .ZIP file as the Code entry type. Upload the deployment package for this tutorial.

Environment Variables

Scroll down to the Environment variables section and set the FORMSMARTS_WEBHOOK_KEY environment variable to your FormSmarts Webhook Key. You can find your Webhook Key in the Security Settings of your account.

Save changes.

Add Permissions to Allow Lambda to Publish to SNS

The Lambda function can't access other cloud services unless we explicitly allow it to do so. The code relies on the Simple Notification Service (SNS) to send the SMS message, so we need allow to the function to publish to SNS.

  • Navigate to Identity and Access Management (IAM). In the Roles pane, locate the LambdaWithSns role you've created earlier.
  • Click Add inline policy
  • Enter SNS as the Service
  • Expand Access level
  • Check Publish
  • Under Resources select All resources
  • Review and save policy

Create an API Gateway Mapping to Lambda

Lambda runs code when an internal cloud event happens. To trigger an event when an HTTP request is received, we need to use API Gateway, another AWS service.

Locate API Gateway in the Services menu at the top of the screen and create a new API.

  • Select the New API option
  • Set FormSmarts-Webhook as the API name
  • Set Endpoint Type to Regional
  • Click Create API

Create a POST Method

  • Use the Actions drop-down list to create a POST method
  • Set Integration type to Lambda Function
  • Set Lambda Function to FormSmartsWebhookListener or whatever you named the function earlier
  • Check Use Lambda Proxy integration box
  • Click Save
  • Accept the warning that you are about to give API Gateway the permissions required to run a Lambda function.

Deploy the API

  • Use the Actions drop-down list to deploy the API
  • Create a new deployment stage called 1
  • Enable throttling to 1 request per seconds, allowing bursts of 5 requests.
  • Click Save Changes

Keep the Invoke URL shown at the top of the screen somewhere, you'll need it next to configure the webhook on FormSmarts.

Set Up the Webhook on FormSmarts

Follow the instructions in the webhook documentation to register the URL given by API Gateway as the callback URL for the form you've created earlier.

The form should now send an SMS message when you submit it.

Note: At the time of writing, AWS offers 100 free SMS to US numbers per month as part of their free tier.