Monday, 22 May, 2023 UTC


Summary

If you have multiple departments and use cases running through a single WhatsApp sender and your application needs help understanding which message your customer is replying to, this post is for you.
In this tutorial, you will learn how to use the Reply Context feature from Twilio Programmable Messaging combined with Twilio Sync to preserve message context and know precisely what a customer is replying to when you receive an inbound message.
A Brief Intro to WhatsApp Replies
In the past, it was not reasonable to expect the end user to use the "Reply Message" feature on WhatsApp when communicating with businesses, as placing that responsibility on them would typically lead to bad UX. However, when WhatsApp released its "Quick Reply" feature (commonly referred to as "Buttons"), the situation drastically changed. When users select one of the Reply options, the app automatically sends a message to the business replying to the original one. You can now safely assume most messages will come with that associated context.
Imagine you send two messages to your customers. The first one welcomes the user and asks if he wants to know more about your company. The second contains promotional information about a specific product, having its own call to action. When the customer interacts with one of them by selecting one of the options, Twilio provides the information on which message they selected, and you can take different actions for each one.
Prerequisites
  • A Twilio Account
  • A Production WhatsApp sender (you can learn how to create one here)
  • Access to the Twilio Content Editor/API (if your account doesn't have it yet, you can reach out to your Account Executive or contact our Digital Sales team)
  • A Messaging Service with your WhatsApp number added to it (required to work with the Content Editor/API)
  • Twilio Serverless Toolkit
  • Node.js and yarn (alternatively, you can use npm)
With every requirement set up, you are ready to go.
Getting Started
First of all, let's set up your environment:
mkdir whatsapp-send && cd whatsapp-send yarn init -y 
Next, install the Twilio Helper Library and the dotenv package:
yarn add twilio dotenv 
Create two new files inside your whatsapp-send directory.
touch index.js && touch .env 
Open the .env file and add the environment variables according to the example below:
TWILIO_ACCOUNT_SID= TWILIO_AUTH_TOKEN= SYNC_SERVICE_SID= SYNC_MAP_SID= MESSAGING_SERVICE_SID= 
These variables store your Twilio Credentials, as well as some relevant SIDs. Leave these blank for the moment. You'll get back to these in a minute.
Creating Content
Before writing code, you must create some content to send. Head to the Content Editor, click Create New, input quick_reply_template as the name and English (US) as the language. As for the content type, select Quick Reply.
Click Create to see a new screen to provide details on your content, following the example below. Remember to click the Add Variable button instead of typing their references directly to enable the Sample Content boxes.
Keep in mind that for WhatsApp Business Initiated messages, you'll need approval from Meta. In this case, click Save and submit for WhatsApp approval, select Utility as the type of your message, and wait for its status to change. The approval process typically takes a few minutes but can take up to a business day.
Copy the Template SID. You'll need it for sending the message.
You can check our official documentation for more information. The documentation explains all content types beyond those used in this example and how to use variables best. You can also check our Support Article on what kind of message you should select when asking for approval.
Let's Write Some Code
The next step is to send a sample message registering the message context within Twilio Sync. But first, let's fill in the environment variables you left blank before.
Head to your main dashboard within the Twilio Console and find your Account SID and Auth Token. Copy and paste these values into the .env file you created earlier as the variables TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN values, respectively.
Next, head to the Twilio Sync dashboard. Create a new service by clicking Create new Sync Service, name it "Whatsapp Messaging Context", and you'll be directed to its page. Copy the Service SID on the right and paste it into the SYNC_SERVICE_SID variable.
Click Maps on the left menu, and then Create a new Sync Map. Input "messages" as the Unique Name and a Time-to-live (TTL). Leave the latter blank unless you want the map to expire at some point (this configuration is for the whole map, not its items).
You'll now see the Sync Map page. To retrieve its SID, click Back to Maps and copy the Map SID. Paste it into the SYNC_MAP_SID variable.
Finally, you'll retrieve your Messaging Service SID. You can follow our official documentation if your WhatsApp number is not already a part of one. To get the SID, head to the Messaging Services dashboard, copy the SID that includes your WhatsApp number, and paste it into the MESSAGING_SERVICE_SID variable.

Sending the Message

Open up the index.js file you created earlier in your favorite text editor. Copy and paste the following code into this file, replacing placeholders with your actual values for phone numbers and SIDs):
// Import required modules require('dotenv').config(); // Load environment variables from a .env file into process.env // Set up Twilio client const client = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); const phoneNumber = '+131646131325' // TODO change this to your personal WhatsApp number const contentVariables = { "1": "Mark", "2": "Twilio", "3": "yes", "4": "no" }; const contentSid = 'HXxxxx'; // TODO change this for the Content SID you want to use // Function to send a single message using Twilio Programmable Messaging const sendSingleMessage = () => { // Send the message through the Twilio client return client.messages .create({ from: process.env.MESSAGING_SERVICE_SID, contentSid, contentVariables: JSON.stringify(contentVariables), to: `whatsapp:${phoneNumber}` }) .then(message => { console.log(`Message ${message.sid} sent to ${phoneNumber} `); // Log the sent message return message; }) .catch(err => { console.log(err); // Log any error in message creation }); }; sendSingleMessage(); 
The contentVariables object contains the values for all variables defined during content creation. In this example, you'll give your customer a name and define the company's name, along with ID definitions for the reply buttons. Unused variables will be ignored, and variables not passed as parameters will be sent as the placeholders defined upon content creation.
Before sending the message, make sure that Meta already approved your template.
Run the code:
node index.js 
You should receive a message like this:

Registering the Context

Now that you learned how to send a message using predefined content, it is time to register that you sent it to the user.
Copy and paste the following code into your index.js file:
// Function to register the message using Twilio Sync const registerMessage = (message) => { // Create a Sync Map Item with the message return client.sync.v1.services(process.env.SYNC_SERVICE_SID) .syncMaps(process.env.SYNC_MAP_SID) .syncMapItems .create({ key: `${phoneNumber}_${message.sid}`, // Use the phone number and message SID as the key data: { phoneNumber, contentVariables, contentSid } }) .then(mapItem => { console.log(`Map Item registered with SID ${mapItem.mapSid}`); // Log the registered map item }) .catch(err => { console.log(err); // Log any error in map item creation }); }; 
And add a then callback to sendSingleMessage():
sendSingleMessage() .then((message) => { registerMessage(message); }); 
Rerun it. Now Twilio stores the phone number, the content SID, and the content variables in Twilio Sync.
Processing Users' Replies
When the user replies to the sent message, you need to infer the content the reply references and need a way to process the reply.
You'll create a Twilio Function using the Serverless Toolkit to do that. Run:
twilio serverless:init context-helper --empty cd context-helper 
Open the pre-created .env file and paste the SYNC_SERVICE_SID and SYNC_MAP_SID with the same values as before.
Create a file get-context.protected.js inside the functions directory, and paste the following code, replacing the placeholder with your content SID:
exports.handler = async function(context, event, callback) { const client = context.getTwilioClient(); const { OriginalRepliedMessageSid, From, ButtonPayload} = event; const phone = From.split(':')[1]; try{ const twiml = new Twilio.twiml.MessagingResponse(); client.sync .services(context.SYNC_SERVICE_SID) .syncMaps(context.SYNC_MAP_SID) .syncMapItems(`${phone}_${OriginalRepliedMessageSid}`) .fetch() .then(result => { console.log(result.data); const contact = result.data; switch(contact.contentSid){ case "HXxxx": // TODO: replace this placeholder with your content SID if(ButtonPayload === 'yes'){ twiml.message("Thank you!") } else if(ButtonPayload === 'no'){ twiml.message("No problem. We will not bother you."); } else{ twiml.message("Sorry. I didn't understand"); } break; default: twiml.message("We couldn't find a context with you. What would you like to talk about?"); } callback(null, twiml); }) .catch(err => { if (err.code === 20404) { // Twilio error in case no context is found twiml.message("We couldn't find a context with you. What would you like to talk about?"); callback(null,callback); } else { console.error(err); callback(err); } }); } catch(err){ console.error(err); callback(null, err); } } 
Deploy the function to your Twilio account. Run this code in the project directory of the Twilio Functions.
twilio serverless:deploy 
Every time a user selects one of the quick reply options, Twilio is going to receive an incoming message with three particular parameters:
  • ButtonText
  • ButtonPayload
  • OriginalRepliedMessageSid
The ButtonPayload parameter contains the id defined for each button. In our example, it can be "yes" or "no". The OriginalRepliedMessageSid parameter is the original message SID containing the quick reply buttons.
This function takes the message and looks for any context in Twilio Sync, sending a message back to the user depending on his answers.

Adding New Content

You learned how to create content and process message for that specific content. You can now follow the same steps to build new message templates, adding the newly created content SIDs to the switch within the Twilio Function.
Additionally, you can use our Studio to make the building more user-friendly, allowing non-developers to add new content.
Conclusion
You can now send multiple messages to your customer base and understand which message they are referencing when they reply. To make this app production-ready, here are a few suggestions:
  • Switch the Auth Token authentication to API Keys;
  • Use the Content Editor to create the auto-responses. This will reduce your maintenance efforts when adding new content.
You can find an improved version of this sample code in this Github repository. This solution takes the same logic and extends it to send multiple messages in bulk based on a CSV file versus just sending one message at a time.
This is a great start. To improve your WhatsApp communication, consider looking at the following articles:
  • How to Transcribe Audio Messages on WhatsApp using OpenAI Speech to Text;
  • Create a WhatsApp Bot to Discover Restaurants using Twilio and Node.js;
  • How to Build an InvestorGPT AI Chatbot on WhatsApp with Django, Twilio, and the ChatGPT API.
Biography
Gabriel Oliveira is a Solutions Architect for Latin America. He has been working with the WhatsApp Business API since its release in 2018 and with digital communications since 2011. He focuses on improving customer experience in digital channels. You can reach out to him at goliveira [at] twilio.com.