How to build a Text Message Autoresponder
Having read our A2P Best Practices, you understand the importance of building a system that is able to process and perform actions based on specific keywords that may be received. Keywords like “HELP” and “INFO” should be used to provide more information about the identity of the sender, while “STOP”, “START”, and other opt in/out words should be used to opt users in and out of messaging. In this blog, we’ll discuss three components to consider when building an SMS Responder as well as provide sample code to get you going on one of your own!
- Provide the ability to send predetermined and automated responses after certain phrases or keywords have been received.
- Typically used for opting in or out of a messaging subscription, but can be expanded to a variety of use cases such as coupons and fundraising.
Component #1: Keyword Management
The first component of an SMS Responder is a place to manage keywords. This should be a place for both technical and non-technical users to create and manage keywords and responses without having to touch any code. In the sample code below, I use a Google Sheet to serve as a quick and easy user interface for creating these keyword/response pairs.
|Keyword||Response||Location||Phone Number||Opt Out|
|Stop||You replied with the word “stop”, which blocks all texts sent from this number. Text back “unstop” to receive messages again.||*||*||T|
|Unstop||You have replied “unstop” and will begin receiving messages again from this number.||*||*||*|
|Info||This is an autoresponder demo||*||*||*|
|Info||This is info about Test Location||Test Location||*||*|
|Info||This is info about +12345678901||*||+12345678901||*|
|*||This is the catch all response||*||*||*|
When thinking about what keywords to create, I like to classify keywords into two categories: simple response and action response. All keywords will have some response, but specific keywords will also perform an action. Keywords like “HELP” and “INFO” are examples of simple response keywords, whereas “STOP” and “START” are action response keywords as the action of opting a user in or out of the service is done in addition to sending a response. We’ll dig into opt in/out management in the next section.
Another thing to consider when creating an SMS Responder is the ability to create specific keyword & response pairs based on certain parameters. For example, rather than having to set up a user interface for each number or group of numbers, you may find it more beneficial to provide your users with a parameter like “Phone Number” or “Location”. “Location” is a term within Bandwidth that translates to a logical grouping of phone numbers. An example of when this would be beneficial is if “Company X” had unique phone numbers for each of their use cases. The “HELP” response should be unique to each use case, but the opt out mechanism may be the same irregardless of the use case.
Component #2: Opt In/Out Management
Ensuring that your end users have the ability to opt in and out of receiving messages is pivotal to staying in the good graces of the carriers. For toll-free SMS, “STOP” and “UNSTOP” (as well as START, ARRET, and NONARRET) are built in keywords and there’s no need for you to send an acknowledgement to the consumer. The generic opt-out confirmation message returned to a consumer from the network provider gives instructions on how to opt back into messaging. You should, however, perform the action of adding the user to an opt-out list to prevent sending future messages. While you’ll stay compliant since any subsequent message will be blocked and never make it to the end user, it will count as a billable message.
Outside of toll-free SMS, you have more flexibility in terms of how you’d like to support opt-in and opt-out. For example, you may choose to use “UNSUBSCRIBE”, “END”, or “RESUME”. In addition to choosing your own opt in/out keywords, you also have the freedom to be more creative with the opt-out response and add a more brand focused message.
Regardless of the messaging channel, the key is to educate your users on how to opt out by including information in the initial message as well as every so often as a reminder. While it may be viewed as an inconvenience to include this information, it’s much less of an inconvenience compared to working with the carriers to remove a phone number from a blacklist due to users not being able to, or not knowing how, to opt out and choosing instead to flag your messages as spam.
In the sample application below, “Opt Out” and “Opt In” are parameters that can be set on any keyword. If one of these keywords is received, the desired response will be sent and the user’s phone number will be added to or removed from the “Opt Out” tab. Responses will not be sent for “Opt Out” keywords on toll-free numbers based on the built in functionality described above.
def opt_out(service, sending_number, receiving_number) opt_range = "'Opt Out'!A:B" opt_value_range_object = Google::Apis::SheetsV4::ValueRange.new(majorDimension: 'ROWS', values: [[sending_number, receiving_number]]) opt_append = service.append_spreadsheet_value(SPREADSHEET_ID, opt_range, opt_value_range_object, value_input_option: 'RAW') end
Component #3: Real Time Message Log
The last component we’ll discuss is building a message log to access the inputs and outputs of our messaging service in real time. A message log can be useful in a variety of ways, including debugging your keywords and responses as well as for analytics of how your consumers are communicating with you. Bandwidth provides several mechanisms for retrieving information about your messages, including our Messaging Insights Logs, our GET Messages API endpoint, or our Message Detail Records. These solutions by design do not provide the ability to retrieve the content of the messages, so we’ll discuss how to store that content externally.
We’ll be utilizing the callback/webhook events Bandwidth sends and the API requests we make to create an external message log that includes the content of the message. When we send out a message, we will store the content of the message as well as the data returned in the HTTP response. That HTTP response will most notably include a Message ID. We will use that Message ID to correlate the message-delivered or message-failed callback/webhook we receive. When receiving a message, all of the information needed will be in the message-received callback/webhook. As this event is the only place you would be able to retrieve the content of an inbound message, Bandwidth will retry sending the event over a 24 hour period until a HTTP 2xx code is returned.
In the sample application, we’ll use a separate Google Sheet tab to store all of this information.
def log_outbound_callback(callback, service) row = find_id(callback["message"]["id"], service) + 2 type_range = "'Message Log'!G" + row.to_s description_range = "'Message Log'!H" + row.to_s error_range = "'Message Log'!I" + row.to_s completed_range = "'Message Log'!K" + row.to_s type_value_range_object = Google::Apis::SheetsV4::ValueRange.new(values: [[callback["type"]]]) description_value_range_object = Google::Apis::SheetsV4::ValueRange.new(values: [[callback["description"]]]) error_value_range_object = Google::Apis::SheetsV4::ValueRange.new(values: [[callback["errorCode"]]]) completed_value_range_object = Google::Apis::SheetsV4::ValueRange.new(values: [[callback["time"]]]) type_update = service.update_spreadsheet_value(SPREADSHEET_ID, type_range, type_value_range_object, value_input_option: 'RAW') description_update = service.update_spreadsheet_value(SPREADSHEET_ID, description_range, description_value_range_object, value_input_option: 'RAW') if !callback["errorCode"].nil? error_update = service.update_spreadsheet_value(SPREADSHEET_ID, error_range, error_value_range_object, value_input_option: 'RAW') end completed_update = service.update_spreadsheet_value(SPREADSHEET_ID, completed_range, completed_value_range_object, value_input_option: 'RAW') end
One common pitfall of setting up an SMS autoresponder is the endless loop. If your service initiates or receives a message from another autoresponder, the two services could begin relaying messages back and forth quicker than competitive table tennis. The suggestion we’d make to all of our customers is to only send the autoresponder message once – not every time a message is received from the number. This would be per keyword over a set period of time, as it should be expected for an end user to send multiple messages in a session to learn more about your service. The primary scenario we are hoping to avoid is both services receiving messages not mapping to a keyword, and thus sending a default reply. The other less likely scenario would be a number being spammed by an end user. In both scenarios, putting limits on when to respond to an incoming message will help avoid getting caught in an endless loop. Bandwidth does not currently have a loop or throttle limit or automatic detection so it is up to you to implement preventive measures.