Writer Name: Aniket Bhattacharyea
Customer feedback is a vital part of a business’s success. Efficient and engaging communication with customers is the key to increased retention and customer satisfaction. Short message service (SMS) surveys are an excellent way to collect customer feedback and convert it into valuable insights.
Using an SMS for customer surveys is quicker and more efficient than other commonly used communication channels, like emails, phone calls, or app notifications. Emails can be marked as spam and never opened by the customer. It also requires an active internet connection on the consumer’s side. Phone calls can be time-consuming and inconvenient, and most customers are likely to consider them a nuisance. App notification requires the customers to install your app, which limits the reach of the survey.
On the other hand, SMS is cheap to set up and send, can be received by anyone with a phone, and can be answered at the customer’s convenience. According to a 2017 survey, 58 percent of customers prefer human interaction while communicating with a business. SMS’s informal, conversational nature emulates precisely that human touch, so the customer feels valued and is more likely to engage with the SMS.
In this article, you’ll learn why you might want to utilize SMS surveys and how to build one with Octopush. Octopush is an SMS platform offering the ability to send bulk SMS, voice SMS, and much more.
Use Cases for SMS Surveys
SMS surveys are primarily used to gather customer feedback and insights. They can be used after an event, after a support call, or after someone makes a purchase to analyze the customer’s satisfaction level and take appropriate steps.
Companies can use SMS surveys for brand awareness as well. You can use them to figure out how a customer found your brand and what their perception is of its performance. This feedback can help you figure out what areas need improvement and where your organization is performing well.
Employee engagement feedback is another way you can use SMS surveys. They can reveal the satisfaction level of your employees and areas where you can improve to create a better workplace.
Build and Send an SMS Survey with Octopush
This section will teach you how to send SMS surveys with Octopush. Octopush provides API and client libraries in many languages, like Node.js, PHP, and Python. In this article, you will create a Node.js application that can send SMS surveys and collect responses.
If you want to clone the project down and follow along in your editor, you can use this GitHub repo.
Before you begin, you need to have Node.js installed on your computer.
Install Octopush
To register, visit the Octopush registration page, fill in your account details, and click the Sign Up button. After you’ve finished, you’ll receive five free SMS credits.
Note that while you can use Octopush’s SMS campaigns right away, to receive replies, you’ll need to buy a virtual number. You can turn this service on by visiting the Additional services page in the Octopush dashboard and toggling the switch under Virtual numbers.
From the Order menu, select Virtual numbers and purchase any number you like. The only thing to keep in mind is that while virtual numbers can receive messages from anywhere in the world, they can only send messages to the country where they’re registered. So you need to buy the virtual number in the country where you want to send SMS.
It takes at least one week to set up your virtual number. Once it’s set up, you’ll receive an email, and a section named My virtual numbers will show up on your dashboard where you can find the virtual number you just bought.
Now, it’s time to start coding. Click on API & Integrations then HTTP API Credentials and write down the API key.
Create the Node.js project and install the required dependencies:
mkdir sms-survey && cd sms-survey npm init -y npm install octopush express sqlite3
The app will use the Express library to handle the HTTP requests and the SQLite library to store the responses.
Create a file named config.js
with the following code:
const octopush = require('octopush') module.exports = { 'user_login': '********@gmail.com', 'api_key': '**********', 'sms_type': octopush.constants.SMS_WORLD, 'sms_sender': '********', 'sms_mode': octopush.constants.INSTANTANE }
Use your email ID and API key in the user_login
and api_key
fields, respectively. The code also sets up some default options for the SMS. The SMS_WORLD
type denotes that the SMS is possibly sent outside mainland France. The INSTANTANE
mode denotes that the SMS is sent immediately. The sms_sender
field is used to set the sender of the SMS. In this field, you’ll need to use your virtual number along with the country code.
Create Questions for the Survey
Now you need to create the questionnaire for your survey. To do this, create a file named questions.js
and add the following code:
module.exports = [ "Welcome to our short survey! Can you please tell us your name?", "Did you attend our previous workshop on 30/12/2021?", "On a scale of 1-10, how would you rate your experience?", "Thank you for participating in the survey"]
The code above simply exports an array of questions that are then used to create the survey. You can add as many questions to the array as you want. In this example, there are three questions and one concluding text.
Write the Code
Now, write the actual code for the survey that goes inside index.js
.
Start with the necessary imports:
const express = require('express') const app = express() const port = 3000 const octopush = require('octopush') const config = require('./config.js') const questions = require('./questions') const sqlite3 = require('sqlite3').verbose() app.use(express.json())
Create an instance of the SMS
object using the credentials from the configuration object:
const sms = new octopush.SMS(config.user_login, config.api_key)
Now you need to set up the database. The following code sets up an SQLite database named replies.db
in the current directory:
let db = new sqlite3.Database('./replies.db', (err) => { if (err) { console.error(err.message); } console.log('Connected to the replies database.'); })
If replies.db
does not exist, it will be created.
You also want to create the necessary tables in the database; in this case, two tables are necessary. The current_state
table keeps track of the last question sent to each participant so that when a reply is received, it can send the next question. The replies
table stores each participant’s received replies for each question.
Use the following code to create the tables:
db.serialize(() => { db.run(`CREATE TABLE IF NOT EXISTS replies( recipient_number TEXT, question_id INTEGER, reply TEXT, replied_at TEXT ) `).run(`CREATE TABLE IF NOT EXISTS current_state( recipient_number TEXT PRIMARY KEY, question_id INTEGER )`) })
Now it’s time to define the routes. The first route, /start
, will be used to start the survey. The recipients
parameter in the request body is an array of phone numbers. The first question is sent as an SMS to each of these phone numbers, and a corresponding entry is inserted into the current_state
table.
app.post('/start', (req, res) => { sms.set_sms_text(questions[0]) sms.set_sms_recipients(req.body.recipients) sms.set_sms_type(config.sms_type) sms.set_sms_sender(config.sms_sender) sms.set_sms_request_id(sms.uniqid()) sms.set_sms_mode(config.sms_mode) sms.set_option_with_replies(true) sms.send((e, r) => { if(e) { console.log('Error:', r) res.status(500).send(r) } else { const stmt = db.prepare("INSERT INTO current_state VALUES(?, ?)") req.body.recipients.forEach((recipient) => { stmt.run(recipient, 0) }) stmt.finalize() res.send(`Success: ${JSON.stringify(r)}`) } }) })
The following route is /reply
. Octopush will make a POST request to this route whenever a reply is received. Start with the route declaration:
app.post('/reply', (req, res) => { })
From the request body, you need three parameters for this tutorial:
- The
number
parameter contains the participant’s phone number. - The
text
parameter contains the reply received. - The
reception_date
parameter contains the date and time when the reply was received.
Use the following code to extract the parameters:
app.post('/reply', (req, res) => { const { message_id, number, text, sim_card_number, reception_date } = req.body })
To begin, you need to check the current_state
table to find out which question the participant is currently answering, if any at all.
app.post('/reply', (req, res) => { const { message_id, number, text, sim_card_number, reception_date } = req.body db.serialize(() => { db.get(`SELECT question_id FROM current_state WHERE recipient_number = ?`, [number], (err, row) => { if(err || !row) return res.sendStatus(200) // Participant record found }) }) })
Note that in case of an error or no record of this participant in the database, a status code
200
with an empty body is sent as a response. Octopush requires this.
If a participant’s record exists, you need to insert their reply into the database first.
app.post('/reply', (req, res) => { const { message_id, number, text, sim_card_number, reception_date } = req.body db.serialize(() => { db.get(`SELECT question_id FROM current_state WHERE recipient_number = ?`, [number], (err, row) => { if(err || !row) return res.sendStatus(200) // Participant record found db.run(`INSERT INTO replies VALUES(?, ?, ?, ?)`, [number, row.question_id, text, reception_date], (err) => { if(err) return res.sendStatus(200) // No error }) }) }) })
Just like before, a 200
status is returned in case of an error.
After inserting the reply, you need to check if there are any questions left. If there are no more questions, end the API call by sending a 200
status.
app.post('/reply', (req, res) => { const { message_id, number, text, sim_card_number, reception_date } = req.body db.serialize(() => { db.get(`SELECT question_id FROM current_state WHERE recipient_number = ?`, [number], (err, row) => { if(err || !row) return res.sendStatus(200) db.run(`INSERT INTO replies VALUES(?, ?, ?, ?)`, [number, row.question_id, text, reception_date], (err) => { if(err) return res.sendStatus(200) // No error if(row.question_id + 1 >= questions.length) return res.status(200).send('Finished') // There are more questions }) }) }) })
Note that you’re sending a response body to indicate that it’s not an error this time.
The final piece of the puzzle is to send the following question to the participant and update the current_state
table.
app.post('/reply', (req, res) => { const { message_id, number, text, sim_card_number, reception_date } = req.body db.serialize(() => { db.get(`SELECT question_id FROM current_state WHERE recipient_number = ?`, [number], (err, row) => { if(err || !row) return res.sendStatus(200) db.run(`INSERT INTO replies VALUES(?, ?, ?, ?)`, [number, row.question_id, text, reception_date], (err) => { if(err) return res.sendStatus(200) if(row.question_id + 1 >= questions.length) return res.status(200).send('Finished') // There are more questions const next_question = questions[row.question_id + 1] sms.set_sms_text(next_question) sms.set_sms_recipients([number]) sms.set_sms_type(config.sms_type) sms.set_sms_sender(config.sms_sender) sms.set_sms_request_id(sms.uniqid()) sms.set_sms_mode(config.sms_mode) sms.set_option_with_replies(true) sms.send((e, r) => { console.log(e, r) if(e) return res.sendStatus(200) db.run(`UPDATE current_state SET question_id = ? WHERE recipient_number = ?`, [row.question_id + 1, number], (err, row) => { if(err) return res.sendStatus(200) res.status(200).send("Success") }) }) }) }) }) })
Below is the entire content of the index.js
file:
const express = require('express') const app = express() const port = 3000 const octopush = require('octopush') const config = require('./config.js') const questions = require('./questions') const sqlite3 = require('sqlite3').verbose() app.use(express.json()) const sms = new octopush.SMS(config.user_login, config.api_key) let db = new sqlite3.Database('./replies.db', (err) => { if (err) { console.error(err.message); } console.log('Connected to the replies database.'); }) db.serialize(() => { db.run(`CREATE TABLE IF NOT EXISTS replies( recipient_number TEXT, question_id INTEGER, reply TEXT, replied_at TEXT ) `).run(`CREATE TABLE IF NOT EXISTS current_state( recipient_number TEXT PRIMARY KEY, question_id INTEGER )`) }) app.post('/start', (req, res) => { console.log(req.body.recipients) sms.set_sms_text(questions[0]) sms.set_sms_recipients(req.body.recipients) sms.set_sms_type(config.sms_type) sms.set_sms_sender(config.sms_sender) sms.set_sms_request_id(sms.uniqid()) sms.set_sms_mode(config.sms_mode) sms.set_option_with_replies(true) sms.send((e, r) => { if(e) { console.log('Error:', r) res.status(500).send(r) } else { const stmt = db.prepare("INSERT INTO current_state VALUES(?, ?)") req.body.recipients.forEach((recipient) => { stmt.run(recipient, 0) }) stmt.finalize() res.send(`Success: ${JSON.stringify(r)}`) } }) }) app.post('/reply', (req, res) => { const { number, text, reception_date } = req.body db.serialize(() => { db.get(`SELECT question_id FROM current_state WHERE recipient_number = ?`, [number], (err, row) => { if(err || !row) return res.sendStatus(200) db.run(`INSERT INTO replies VALUES(?, ?, ?, ?)`, [number, row.question_id, text, reception_date], (err) => { if(err) return res.sendStatus(200) if(row.question_id + 1 >= questions.length) return res.status(200).send('Finished') const next_question = questions[row.question_id + 1] sms.set_sms_text(next_question) sms.set_sms_recipients([number]) sms.set_sms_type(config.sms_type) sms.set_sms_sender(config.sms_sender) sms.set_sms_request_id(sms.uniqid()) sms.set_sms_mode(config.sms_mode) sms.set_option_with_replies(true) sms.send((e, r) => { console.log(e, r) if(e) return res.sendStatus(200) db.run(`UPDATE current_state SET question_id = ? WHERE recipient_number = ?`, [row.question_id + 1, number], (err, row) => { if(err) return res.sendStatus(200) res.status(200).send("Success") }) }) }) }) }) }) app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`) })
Test the SMS Survey
To test the SMS survey, you’ll need to host the server publicly on the internet. You can use a hosting provider to host it on a server or a tool, like Ngrok, to run the server locally and expose it to the internet. Either way, you must have a URL pointing to your server, which the rest of the article will refer to as $BASE_URL
. You should replace any occurrence of $BASE_URL
with the URL of your server.
Once you have the server up and running, visit your Octopush dashboard, click on the Menu button in the top-right corner and navigate to the Callbacks/Webhooks page.
Click on the Edit my callback parameters button, and in the Inbound SMS field, paste $BASE_URL/reply
and save the changes.
Now, whenever you receive a reply, the data will be sent to this URL, which will trigger the reply function in the code.
You need to send a POST request to $BASE_URL/start
with the recipient’s phone number(s) to start the survey. Ensure that the phone numbers are in the same country as your virtual number. To send the request, use a tool like Postman or cURL:
curl -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST "$BASE_URL/start" -d '{"recipients": ["number_with_country_code"]}'
After a few minutes, you should receive the first question, and from here on, you can reply to the questions and continue the survey.
The replies will be stored in the replies.db
database, which you can connect to by running sqlite3 replies.db
. The replies can then be viewed using the SQL query:
select * from replies;
Here’s a sample output:
+12345678|0|Kasper|2022-01-14 14:18:24 +12345678|1|Yes|2022-01-14 14:18:32 +12345678|2|10|2022-01-14 14:18:41
Now you’ve seen how everything comes together to send an SMS survey with Octopush. If you want to see the entire project at once, you can clone the project from GitHub.
Conclusion
SMS surveys can be a great tool to engage with customers and build brand awareness. With Octopush, creating custom SMS surveys is easy. Sign up now for a free account and take advantage of the full power of Octopush.