Photo by Glenn Carstens-Peters on Unsplash
Send SMS messages to many phone numbers using the Twilio API and Spring
Introduction
Sending SMS is an excellent way to get your customers to know of any promotions or sales in our businesses.
Suppose that you're a top-seller t-shirt retailer. A good way of letting a customer know of any discount in your store is by sending an SMS message to that customer. Twilio SMS API lets you send a single SMS very easily.
Now, let's imagine that your customers are very big fans of basketball. Or that a famous NBA player just scored 46 points in a single game, then all of sudden all your customers want to buy his t-shirts. You'll want all your customers to know about a discount on that t-shirt as soon as possible.
In this post, I'll show you how to set up a service to send SMS messages to many customers using Twilio SMS API, Twilio Messaging Service, and Spring.
Pre Requisites
You'll need a couple of things to follow along with this tutorial:
- Java Development Kit (I'm using the latest LTS version, 17.0.1)
- A Twilio Account, a Twillio number , and a Twilio Messaging Service
- IntelliJ IDEA (I'm using the Community Edition version 2021.3), or any other IDE that supports Java
The Implementation
Set up The Twilio Messaging Service
To set up a Twilio Messaging Service go to the console, and click
on the Create Messaging Service
button, under Messaging->Services. Pick any name to it and choose the option Market my services
to its purpose and go to the next step. On the
Step 2, add your Twilio phone number as a sender and go to the next step. The Integration part at step 3 contains some useful configurations:
Incoming messages define how you want to handle incoming SMS messages. You can mark
Drop the message
and ignore incoming SMS messages. Or, you can markDefer to sender's webhook
if you want to invoke the SMS sender webhook. Finally, you can respond to all incoming messages usingSend a webhook
. I marked mine asDrop the message
since I'm not very interested in the customer's response to the discount I'm making. I'm just letting them know.You can also set the
Callback URL
to be the URL that Twilio will send the delivery status of SMS messages sent. I marked mine as empty since I don't have any dedicated URL to handle that.Set the
Validity Period
if you want to not send messages that took a long time to send. The maximum wait time for a message is 4 hours. I left mine as the default of 4 hours.
You can set some information about your business at step 4 to unlock an A2P 10DLC phone number to send a higher volume of SMS messages. You can unlock up to 1 million messages per day, and up to 2 thousand messages per second. That's a huge volume for SMS messages.
In this tutorial, I'll not reach that volume of messages. But keep in mind that as soon as your application grows to that volume, you'll need a dedicated 10DLC phone number to handle scalability.
After completing all steps, your Twilio Messaging Service is set up and ready to be used.
Set up your Spring Application
I'll use a Spring Maven Project with Spring Web dependency to create our API. You can generate your
project here and use the same configurations shown in the image below if you're using JDK17.
If not, set the correct Java version accordingly to the one installed in your machine. You can change the artifact,
name, and description fields as per your desire. I choose shirt-retailer
for this example.
Then, press the generate button and save it somewhere.
Now, let's add
the Twilio Java Helper Library into our project to make the API easier to
work with. Open the project in IntelliJ IDEA and add the following lines to your pom.xml
file:
<dependency>
<groupId>com.twilio.sdk</groupId>
<artifactId>twilio</artifactId>
<version>8.27.0</version>
</dependency>
Also, add the Lombok dependency to pull some useful annotations:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
You'll also need to set some secret values for Account SID, Auth Token, and the Twilio Messaging Service. It is a good practice to set
those values as environment variables. I'll use the application.properties
file to store the variables, and the @Value
Spring annotation to
get them. Pick your account values at the Twilio console and replace them in the application. properties
file:
TWILIO_ACCOUNT_SID=<your_account_sid>
TWILIO_AUTH_TOKEN=<your_auth_token>
TWILIO_MESSAGING_SERVICE_ID=<your_twilio_messaging_service_id>
With that set, we should be good to write some code.
Create the REST API to send SMS messages
Let's start by defining our REST API class. Create a package named controller
and a file named SmsController
inside
of it with the following content:
package com.example.shirtretailer.controller;
import com.example.shirtretailer.model.SmsRequest;
import com.example.shirtretailer.service.SmsSenderService;
import com.twilio.rest.api.v2010.account.Message;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/sms")
@RequiredArgsConstructor
public class SmsController {
private final SmsSenderService smsSenderService;
@PostMapping("/send")
public ResponseEntity<List<Message>> send(@RequestBody SmsRequest request) {
return new ResponseEntity<>(smsSenderService.send(request), HttpStatus.OK);
}
}
This code snippet is defining a POST
API endpoint called /send
that works under the resource /sms
. The API
response is a list of Twilio results of sending an SMS. Forget about the SmsSenderService for now, we'll create that in a moment.
Create a class named SmsRequest
to wrap our API request, and add to it the code below:
package com.example.shirtretailer.model;
import lombok.Data;
import java.util.List;
@Getter
public class SmsRequest {
private List<String> numbers;
private String message;
}
The request object contains only two fields: one to store the list of target numbers, and another to store the SMS
content. The @Getter
annotation from Lombok dependency will generate a getter method for each field.
So far, we have defined an API that does nothing special. Now, let's give some functionality to our application. Create another package named service
. Add to it a class named SmsSenderService
with the following content:
package com.example.shirtretailer.service;
import com.example.shirtretailer.model.SmsRequest;
import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.type.PhoneNumber;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
import static java.util.stream.Collectors.toList;
@Service
public class SmsSenderService {
private final String TWILIO_ACCOUNT_SID;
private final String TWILIO_AUTH_TOKEN;
private final String TWILIO_MESSAGING_SERVICE_ID;
public SmsSenderService(@Value("${TWILIO_ACCOUNT_SID}") String TWILIO_ACCOUNT_SID,
@Value("${TWILIO_AUTH_TOKEN}") String TWILIO_AUTH_TOKEN,
@Value("${TWILIO_MESSAGING_SERVICE_ID}") String TWILIO_MESSAGING_SERVICE_ID) {
this.TWILIO_ACCOUNT_SID = TWILIO_ACCOUNT_SID;
this.TWILIO_AUTH_TOKEN = TWILIO_AUTH_TOKEN;
this.TWILIO_MESSAGING_SERVICE_ID = TWILIO_MESSAGING_SERVICE_ID;
}
public List<Message> send(SmsRequest request) {
Twilio.init(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
return request.getNumbers()
.stream()
.map(number -> Message.creator(new PhoneNumber(number),
TWILIO_MESSAGING_SERVICE_ID,
request.getMessage())
.create())
.collect(toList());
}
}
This service class will do the actual job. First, I've initialized the Twilio client the environment variables defined in the application.properties
file
with the help of the @Value
Spring annotation. Then, I've applied map
operation over the phone numbers contained in the request to
create a new Twilio SMS Message
object for each phone number in the list.
To create a message, I've used the creator
method that contains three arguments. The first argument is the target number. The second one is the unique number of the Messaging Service we've created before. And the third is the SMS content.
Finally, we return the collected results of all SMS messages. The results contain useful information about the SMS delivery like timestamp, errors (if any), version of Twilio API used, and others.
Testing our code
Let's test our API at localhost using the Run
IntelliJ configuration that we've created before, and cURL. Click on the
play button in the top right corner in IntelliJ to start the application. If the application started properly (and
most likely will), open up a terminal and paste the cURL below. Pick some phone numbers that you can see the incoming SMS notification.
curl --location --request POST 'http://localhost:8080/sms/send' \
--header 'Content-Type: application/json' \
--data-raw '{
"numbers": [
"<phone_number_1>",
"<phone_number_2>"
],
"message": "Stephen Currys T-Shirt 50% off only today"
}'
After that, you should be able to see the SMS notification on your smartphone and get your Steph Curry's T-Shirt piece before it runs out of stock! Also, the response of this API lets us know of any failures that happened in SMS delivery and some other useful information. The response that I've got from the cURL using my phone number was:
[
{
"body": "Stephen Currys T-Shirt 50% off only today",
"direction": "OUTBOUND_API",
"from": null,
"to": "<masked>",
"price": null,
"uri": "<masked>",
"status": "ACCEPTED",
"sid": "<masked>",
"numSegments": "0",
"dateUpdated": "2022-03-05T00:39:10Z",
"errorMessage": null,
"accountSid": "<masked>",
"numMedia": "0",
"messagingServiceSid": "<masked>",
"dateSent": null,
"dateCreated": "2022-03-05T00:39:10Z",
"errorCode": null,
"priceUnit": null,
"apiVersion": "2010-04-01",
"subresourceUris": {
"media": "<masked>
}
}
]
For security purposes, I've masked some personal information from the response body.
Troubleshooting
Since we're testing with a Twilio Trial phone number, you need to verify the numbers that you're trying to send an SMS to. If you do not verify, you'll get an error when sending that request. To verify a number, go to your console, add the desired number and approve it using the two-factor authentication sent to that phone number.
Conclusion
As retailers or sellers of any kind of product, we need a way to send notifications to our customers of discounts. In this tutorial, you learned how to achieve that with an API to send SMS messages to various phone numbers at once using Spring Boot, Twilio SMS API and Twilio Messaging Service. If you have any questions please reach out to me in the comments. Hope you enjoyed it and see you at the next one!
Pedro Lopes is a backend engineer at a Brazilian digital bank. He's a specialist in cloud computing, distributed systems, and Java coding. He likes to write and read in his spare time. He's also a big fan of sports, especially basketball.