High Volume SMS and PHP

Pbmacintyre
RingCentral Developers
13 min readJun 3, 2021

So you have a large number of clients that you want to communicate with by sending them SMS text messages. In the past this has proven problematic with regulatory constraints and throttles keeping the amount of messages per request low so as not to overwhelm the cellular networks and annoy recipients. When you think about major retailers that want to get their messages out during high sales traffic times like Christmas or Easter or Mother’s Day or Valentine’s Day or Halloween or Black Friday or back to school or … you can imagine there is a high demand for this kind of messaging. Another use case would be a leading automobile manufacturer needing to inform their vehicle owners of a safety recall or on a less stressful matter — informing them of a sale on tires. Think also of a credit card company trying to warn their customers of ongoing fraud campaigns or offers on new account creation promotions where you get a free toaster. As you can see, there are so many instances where this need for mass SMS messaging is in demand. Think of other major industry verticals and their many communication needs as well: health care, education, real estate, travel (eventually), legal services, high tech, research, and so on. However, with current limits around 200 messages from a single source phone number per day depending on the provider, this limitation is not conducive to the situations described above.

RingCentral’s high-volume SMS offering allows for up to 250,000 text messages sent per day with a sub-limitation of up to 50MB data size per API request from a single commercial SMS phone number. Before we look at some code samples here, let’s review these ground rules in a little more detail. Firstly, you need to have a high-volume SMS enabled phone number. This means that your number has to be provisioned as a high-volume A2P type of number. A2P stands for “application to person” and this means that the SMS request is not being managed by an individual cell phone (human) but by software. Additionally, it means that the receiver of the SMS message is not expected to reply to the sent message. One example of this would be a TV talent show where viewers would text in their vote to endorse the performer they like the most. Software would be used to receive the SMS votes and count them with no replies being sent out to the senders. Alternatively, we can see how our scenarios described earlier also fit within these parameters with software sending messages out to a long list of recipients and not expecting a reply of any kind. Basically it is a one-way SMS communication with software on either end of the information thread. As mentioned earlier, additional rules apply pertaining to the volume and data load in play. Besides the 250,000 daily message limitations, the size of the content per API request (payload) also needs to be below 50 megabytes. So with those rules laid out let’s take a look at some samples with the PHP web development language in mind.

Connect to RingCentral’s API

First we will take a look at a basic SMS message and how to connect PHP to the RingCentral SMS API. You will need a free RingCentral Developer’s account in order to start the testing and use the sample code in this article. So go to developers.ringcentral.com and create a new account or access your existing account and then create a new application for testing. When you create the account be sure to have “SMS” selected in the “App Permissions” section. Also, with access to the current beta program, you will have to select “A2P SMS” if you want to test the High Volume code found later in this article as the high-volume tasks are performed with a different API endpoint. More on that later.

For detailed guidance on how to create one of these testing apps in the RingCentral API Developer environment read this blog article.

After you have the setup done you will need to use the API and application access credentials also found in the developer account area where you defined the application. I store these credentials in my database so that they can be encrypted if needed, retrieved quickly, and easily changed if desired. The following screenshot shows the structure of my control database table. This sample is not using encryption on my passwords or other sensitive data for readability and to make my code samples less complex.

I have created a functions file in my PHP application where all my RingCentral actions are handled. I call it ringcentral-functions.inc and my credentials access function looks like this:

The code for this article is here: https://github.com/pbmacintyre/RC-HV-SMS

I specifically call this function “sdk_sandbox_credentials” so that I know for sure I am getting the credentials for the sandbox testing area. I collect all the needed information and then return it as an array to complete the function.

The next thing we need to do is to use these credentials to open a connection to the API through the SDK. I have another function called ringcentral_invoke_sandbox_sdk() that does this for me. You will notice that I call the credential collecting function from within this function. Also, notice that my first line of code calls in the API library with the “require” directive. This is needed to allow for the local work of the API and to access its internal mechanisms.

Now that I have access to my application and the RingCentral API with these two functions I can build my SMS code to trigger the connections required and to send out a simple SMS text message.

The beginning of my code looks like this:

Here you can see that I access my functions file and connect to my database, then I call the SDK invocation function and test to see if what is returned to me is indeed an object that I can work with. If something fails along the way I can send out error messaging and terminate the running code if needed.

In my sample code I then reconnect to the database and get the phone number that the RingCentral development platform had assigned to my app for sending SMS messages. You can hardcode this if you want to as it is a number that likely won’t change once it is set but if you are planning to use this code for multiple applications or for multiple code bases then it makes sense to store and retrieve it in this way. The code for this function will differ slightly from how we will use the ‘from’ number in the production area, so I am using a function called: “ringcentral_get_sandbox_from_phone” and passing in the control_id for the specific data row where I have my sandbox information stored.

Standard SMS

Next we prepare the list of who we are sending these SMS messages to and what the actual text message will be. We can send the same message to multiple recipients with the basic SMS endpoint but we are still limited to ~200 messages per day so this is not ideal for the larger volumes of output that we are trying to achieve; and we can only send out one non-changing message per request. The code file for this portion of the article is called “StandardSMS.php”.

A small work around for this limitation on the text of the message could be to put the SMS message as a whole into a PHP loop and call the API endpoint repeatedly… but then you are in danger of infringing on other throttles like the number of messages per minute, and so on. Suffice it to say this is not an ideal approach.

To eventually call the endpoint and actually send out the text message your code should look something like the following code block. Using a try / catch structure we can react to any type of potential failure communicating with the API. Notice that the ‘To’ parameter is being loaded with an array of the phone numbers of those who will be recipients of this message with the $to variable. Also, note the API endpoint here is pointing to the SMS aspect of the SDK.

When the code is run, if all goes as planned, you should have sent the following text to the specified phone number(s).

Our code should also send out a few messages to the browser so that we know the program worked.

If the attempt to send an SMS message fails we can output the response within the “catch” portion of the code to see if we have issues with our sending attempt. The first few lines of a typical failed response could look like the following; this will look different depending on the cause of the error, but the basic information layout will look the same:

The High Volume approach

The overall purpose of this article is to show how to take advantage of the high volume options that RingCentral has to offer. Limitations are much broader as we saw in the earlier parts of this article, but they are limitations nonetheless. So we will adjust our code to suit and add on a few more constraints and functions to help protect our newer higher limits. The major change here is that we are calling a new API endpoint. The code for which follows:

$sdk->platform()->post(“/restapi/v1.0/account/~/a2p-sms/batch”, $requestBody);

The $requestBody variable will hold an array of all our information being sent out, so let’s look deeper into how we would build this up.

The code file for this first high volume example is called “HighVolumeSMS_1.php”.

First, let’s look at some more database SQL techniques that we can use to help keep us within the High Volume SMS limitations. We can use the ‘LIMIT’ clause on our SQL command to only bring in the amount of records that we want. So we will select 8,000 mobile numbers, 2,000 less than the overall limit just to be cautious. The syntax follows:

$sql = “SELECT `mobile_number` FROM `ringcentral_clients` LIMIT 8000” ;

The ‘LIMIT’ clause can be used to bring in a defined number of records. This is only the beginning consideration here really because if you plan to send out the daily limit you should manage what batch you have sent out versus what is still to be sent and also ensure that you are not sending the same message to the same mobile number (avoiding overlap). This level of control is beyond the scope of this article, but you should be aware of this point of consideration when the time comes to go into production on your high volume SMS app.

With this information in hand you can now build the structure of the SMS messages that will be sent out. The following code shows the first approach where everyone gets the same text content message. First we run the actual SQL query and then use a while loop to build an array of all the recipient phone numbers. Lastly, we complete the building of the $requestBody array with the number that will be sending out the texts.

Note: It is important to note that with the High Volume SMS phone number being provisioned specifically for this purpose that it has to be used as the “from” number in addition to the “username” number that you create the app with in the developer area. You will see this distinction in the additional function called “ringcentral_get_prod_from_phone” in which I pass in the database record number I am after and return the ‘ringcentral_from’ field value.

Our outgoing message is a short “Testing with the RC HV SMS App” in our case.

The received message should look like that shown in the following image.

The second approach which allows for custom / personalized messages to individual numbers has slightly different code when building the $requestBody array. The code file for this second high volume example is called “HighVolumeSMS_2.php”. Our section of code that is of interest follows:

In our above code example we are selecting our client first name information from the database as well as their mobile phone number. Then we build a different array called $messages with their name being added to the ‘text’ element and ultimately the $requestBody array is built from two sources. The resultant sent message looks like this:

Data load size limitation

The next consideration on limitations is the size of the overall request that you are sending to the API. Here I have created a simple function that counts the characters within the multi-dimensional array to give an estimate of the data load. We also send in the size limit we are wanting to stay within; in our case 45 MB to ensure we are well below the maximum. With a loose equivalent of 1 character = 1 byte my function looks like the following:

This follows the structure of the multi-dimensional array down to 3 levels deep and reports the character count (bytes) of each element then divides it by 1 million to get the estimated megabytes. We return true or false depending on the size limit we are testing for; in our case we are keeping our test level a little lower than the overall limit of 50 MB testing for 45 MB. This is the second parameter that we are providing to the function so we can adjust our testing limit as desired. This function should be called after the whole $requestBody array is built.

The call and test of this function should look something like this:

// check the estimated size of the outgoing request.if (get_MBs($requestBody, 45)) {
echo “The MB Size of the HV SMS request exceeds size limits” . “<br/>” ;
echo “Mega Bytes: “ . $megaBytes . “<br/>” ;
exit();
}

Since this is sample code, we are calling the exit() function here to terminate the code if the size limit is exceeded so as not to send the high volume request inadvertently. Production code would handle this error more gracefully. The complete try / catch code block then looks like this:

If you see an error like this:

string(23) “InsufficientPermissions”[“message”]=>
string(81) “In order to call this API endpoint, application needs to have [A2PSMS] permission”

Or like this:

string(7) “AGW-110”[“message”]=>
string(42) “Internal Server Error. Consult RC Support.”

Then you have either not set up your app to use an outgoing phone number that is A2P provisioned or the app is ready but the phone number itself is not set to be A2P ready. So, be sure to have that final detail taken care of as well. You can use code like the following to make sure your numbers’ status hasn’t changed in provisioning before using it to send out high volume texts.

This code file is called “test-HVSMS-numbers.php” and it should send out a report to the browser like the one that follows, showing the numbers in the account and how they are provisioned.

Work on the server

As you would expect, once the “batch” of high volume text messages is submitted to the RingCentral servers they all don’t go out at the same time. The server sends out the messages in a metered approach to help control output volumes. So if you want to check on the status of a requested batch just pick up the job number from the response object after the initial submission is sent. A single line of code like the following can be added within the “try” clause.

$job_id = $resp->json()->id;

The following image shows the entire response object sent to the browser so you can see any other information that you may want to review once the job is sent to the server.

Then you can store the job id for later reference and query the server to get a status on the progress of the job with code like the following (check-HV-SMS-job.php):

Here you are connecting to the SDK and posting the batch job Identifier to the server with your app credentials. Notice the change of the platform method from ‘post’ to ‘get’. There are currently only two status codes that can be returned — “Processing” or “Completed”. You would receive a response similar to the following if the job was completed:

Final Thoughts

It is also good to note that the RingCentral servers will handle any opt-in / opt-out requests based on “from” and “to” phone number pairings. This means that you don’t have to manage any grafting in or pruning out actions for your contact list at least in this regard. However, it is also a good practice to do list management so that you are not inadvertently sending SMS messages to folks that have opted out. This could happen if you are using the same sending list with a different outgoing phone number on a different app. In order to manage your clients who have opted out you can access the “opt-outs” API portion of the High Volume SMS API. Consider this code sample:

$params = array( ‘from’ => ringcentral_get_prod_from_phone(3) );
try {
$resp = $sdk->platform()->get(“/restapi/v1.0/account/~/a2p-sms/opt-outs”, $params);
echo “<pre>” ;var_dump($resp->json());echo “</pre>” ;

Here we are asking for a response from the server that will show us all the opt-outs that match our “from” phone number. Any number pairings that are returned are considered to have been opted out. The response object will look something like this:

{“records”: [{ “from”: “+19025550100”, “to”: “+12125550100” } ],}…

Conclusion

I hope you have found this article to be helpful if you are considering using the High Volume SMS offering from RingCentral. There are additional resources on the developers site so be sure to go there if you need any more information. RingCentral also offers a prebuilt app here that will allow you to send out high volume SMS texts if you don’t have a need for any major customizations.

Remember, my full code is available on my github account here:

https://github.com/pbmacintyre/RC-HV-SMS

--

--

Pbmacintyre
RingCentral Developers

Peter has over 35 years of experience in IT, primarily in PHP. Author of PHP: The Good Parts; co-author: Programming PHP-4th Ed. Zend certified in PHP 5.3 & 4.0