Welcome to the SMS Hub
Please reference the documentation below in order to utilize the API.
Features/Highlights
Users | Developers |
---|---|
|
|
Limitations
- Both Nexmo and Twilio impose rate limiting of 1 SMS per second. This SMS hub automatically throttles messages for you (in the case of Nexmo), or if you use Twilio, this is done automatically for you (if you create a messaging service - see below)
- In the Billing and Payments section of either site, please be sure to enable Auto reload so that you don't risk running out of credits!!
Getting Started - Workflow/Implementation Details
Twilio | Nexmo |
---|---|
|
|
Getting a Token
You can use a REST client tool (I use Restlet) to make a post to call the endpoint visually (click for larger image):
Input for above screen capture:
{ "ProgramSID": "xxx", "ProgramKey": "yyy" }
Here is some sample C# code to generate a JSON web token used for authentication:
try
{
//create a new http client to get a token using (var client = new HttpClient())
{
// perform a json post
client.BaseAddress = new Uri(Request.Url.AbsoluteUri); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
//instantiate a new officeProgram instance OfficeProgram officeProgram = new OfficeProgram
{
ProgramSID = "123",
ProgramKey = "123"
};
//post async, await response
HttpResponseMessage response = await
client.PostAsJsonAsync("api/Login", officeProgram); response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
token = response.Content.ReadAsStringAsync().Result;
// replace "\" from resulting string
token = token.Replace(@"\", string.Empty).Replace("\"", "");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message); return RedirectToAction("Error");
}
if (string.IsNullOrEmpty(token))
throw new HttpException(401, "You are unauthorized");
Using jQuery:
<script type="text/javascript"> $(function () { var dataJSON = { ProgramSID: "123", ProgramKey: "123" }; $.ajax({ type: 'POST', url: '/api/Login', data: JSON.stringify(dataJSON), contentType: 'application/json; charset=utf-8', dataType: 'json', success: function (token, textStatus, jQxhr) { console.log('token: ' + token); SendText(token); }, error: function (jqXhr, textStatus, errorThrown) { console.log(errorThrown); } }); }); </script>
PHP:
<?php function sms_demo() { /************************* BASE VARS *************************/ $phone = '19705551234'; $programId = 'd2b67be0-79a2-49dd-9476-7c793fdc3297'; $programIdentifier = '123'; $programKey = '123'; $api_url = 'https://wsnetdev2.colostate.edu/cwis463/api'; $auth_tkn = ''; /************************* AUTHORIZATION *************************/ // Build post $auth_url = $api_url . '/Login'; $auth_body = array( 'ProgramSID' => $programIdentifier, 'ProgramKey' => $programKey ); // Send post and get response $auth_response = wp_remote_post( $auth_url, array( 'method' => 'POST', 'body' => $auth_body ) ); // Set auth token (and strip wrapping quotes) $auth_tkn = trim($auth_response['body'], '"'); ?>
Sending a Text
The JWT token response is what is used to then be able to access the secured endpoint in the hub (the function to send an SMS).
Input for above screen capture:
{ "Message": "hey there", "PhoneNumber": "19706904544" }
Here's an example using C#:
// if we made it this far, we have a token for an actual test to post a text
message
try
{
//create another new http client using (var client = new HttpClient())
{
// perform a json post
client.BaseAddress = new Uri(Request.Url.AbsoluteUri); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
//if you just want to test passing along a token without logging in, uncomment 2 lines below and comment out line 89
//string manuallyCreatedToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjZ5bVhVVFBmeU5HNnBCamQiLCJuYmYi OjE1NDA1NzQ5MTgsImV4cCI6MTU1NjI5OTcxOCwiaWF0IjoxNTQwNTc0OTE4LCJpc3MiOiJodHRwOi8vbG9jYWxob 3N0OjUwMTkxIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDE5MSJ9.PRpEJKAsAUeyqp8l5rqgxWPmnKT2x098e Njxu2X2Xtw";
//client.DefaultRequestHeaders.Add("Authorization",
manuallyCreatedToken); client.DefaultRequestHeaders.Add("Authorization", token);
//Instantiate a new text message instance TextMessage textMessage = new TextMessage
{
ProgramId = new
PhoneNumber = "19706904544"
};
//post async, await response
HttpResponseMessage response = await client.PostAsJsonAsync("api/TextMessages", textMessage);
Here's an example using jQuery:
function SendText(token) { $(function () { var dataJSON = { ProgramId: "D2B67BE0-79A2-49DD-9476-7C793FDC3297", Message: "my message", PhoneNumber: "19706904544" }; $.ajax({ beforeSend: function (xhr) { xhr.setRequestHeader('Authorization', + token); }, type: 'POST', url: '/api/TextMessages', data: JSON.stringify(dataJSON), contentType: 'application/json; charset=utf-8', dataType: 'json', success: function (token, textStatus, jQxhr) { console.log("Message sent!"); }, error: function (jqXhr, textStatus, errorThrown) { console.log(errorThrown); } }); }); }
PHP:
<?php /************************* SEND TEXT *************************/ // Build post $sms_url = $api_url . '/TextMessages'; $sms_headers = array( 'Content-type' => 'application/json', 'Authorization' => $auth_tkn ); $sms_body = array( 'ProgramId' => $programId, 'PhoneNumber' => $phone, 'Message' => 'This is a text from demo.', 'DateSent' => current_time('mysql') ); // Send post $sms_response = wp_remote_post( $sms_url, array( 'method' => 'POST', 'data-format' => 'body', 'headers' => $sms_headers, 'body' => json_encode($sms_body) ) ); } ?>
More Code Samples - C#
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mail; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Web; namespace QualtricsAPIDemo.Models { public class DSATextMessages { private static string baseuri = "https://wsnetdev2.colostate.edu/cwis463/"; private static string authuri = "api/Login"; private static string posturi = "api/TextMessages"; private static string programidentifier = "xxx"; private static string programkey = "yyy"; private string token = ""; // this changes periodically, it will email the support address when it changes private static string supportaddress = "stuach_web_team@mail.colostate.edu"; private static string fromaddress = "stuach_web_team@mail.colostate.edu"; private static string smtpserver = "smtp.colostate.edu"; private static string smtpport = "25"; //587; private static string smtpusername = ""; private static string smtppassword = "";//no auth credentials required for us, private HttpClient client; public DSATextMessages() { client = new HttpClient(); client.BaseAddress = new Uri(baseuri); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; //client.DefaultRequestHeaders.Accept.Clear(); //client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // If you have no basic authentication, you can skip thses lines //var authToken = Encoding.ASCII.GetBytes($"{authUserName}:{authPassword}"); //client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); // if you just want to test passing along a token without logging in, uncomment 2 lines below and comment out line xx //string manuallyCreatedToken = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjZ5bVhVVFBmeU5HNnBCamQiLCJuYmYi OjE1NDA1NzQ5MTgsImV4cCI6MTU1NjI5OTcxOCwiaWF0IjoxNTQwNTc0OTE4LCJpc3MiOiJodHRwOi8vbG9jYWxob 3N0OjUwMTkxIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDE5MSJ9.PRpEJKAsAUeyqp8l5rqgxWPmnKT2x098e Njxu2X2Xtw"; //client.DefaultRequestHeaders.Add("Authorization", manuallyCreatedToken); //authorization headers are also added after doing the Authorization call. } ~DSATextMessages() { client.Dispose(); } /// <summary> /// Authenticate only needs to be called occasionally when a new token is needed. /// </summary> /// <returns>true for success, but throws an error on failure</returns> private bool Authenticate() { // create a new http client to get a token /* * POST: https://wsnetdev2.colostate.edu/cwis463/api/Login BODY: { "ProgramSID": "6ymXUTPfyNG6pBjd", "ProgramKey": "vUfprLLcKJ6MFBFuCntvLCsHB53xusCp" } */ string authrequest = @"{""ProgramSID"": ""{pi}"", ""ProgramKey"": ""{pk}""}".Replace("{pi}", programidentifier).Replace("{pk}", programkey); // The actual put method including error handling using ( //var content = new StringContent(postData) var content = new StringContent(authrequest, System.Text.Encoding.UTF8, "application/json") ) { try { client.DefaultRequestHeaders.Remove("Authorization"); } catch { /* not there? that's ok. */ } HttpResponseMessage response = null; //await client.PostAsync(authuri, content); Task.Factory.StartNew(() => { //Make async web request response = client.PostAsync(authuri, content).Result; }); int count_tries = 0; while (response == null && count_tries < 60) // delay for up to a minute -> this allows the threading to pop back and forth between tasks { count_tries++; Thread.Sleep(1000); } if (response == null) { throw new Exception("Authentication attempt timed out during request"); } if (response.IsSuccessStatusCode) { string newtoken = null; Task.Factory.StartNew(() => { //Make async web request newtoken = response.Content.ReadAsStringAsync().Result; }); count_tries = 0; while (newtoken == null && count_tries < 60) // delay for up to a 20 seconds -> this allows the threading to pop back and forth between tasks { count_tries++; Thread.Sleep(200); } if (newtoken == null) { throw new Exception("Authentication attempt timed out during response read"); } token = newtoken; token = token.Replace(@"\", string.Empty).Replace("\"", ""); EmailWebTeam("New TM Token", token); try { client.DefaultRequestHeaders.Remove("Authorization"); } catch { /* not there? that's ok. just add them */ } client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); return true; } else { throw new Exception("Authorization failed " + response.StatusCode.ToString() + " " + response.ReasonPhrase); //return false; } } } /// <summary> /// This one can be run asynchronously if you don't care about the result /// </summary> /// <param name="textMessage">This variable is updated with success and result</param> /// <returns></returns> public async Task<TextMessage> SendText(TextMessage textMessage) { /* * POST: https://wsnetdev2.colostate.edu/cwis463/api/TextMessages HEADERS: Authorization Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjZ5bVhVVFBmeU5HNnBCamQiLCJuYmYi OjE1NDA1NzQ5MTgsImV4cCI6MTU1NjI5OTcxOCwiaWF0IjoxNTQwNTc0OTE4LCJpc3MiOiJodHRwOi8vbG 9jYWxob3N0OjUwMTkxIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDE5MSJ9.PRpEJKAsAUeyqp8l5rqgxW PmnKT2x098eNjxu2X2Xtw BODY: { "Message": "Hello world!", "PhoneNumber": "19706904544" } */ TextMessage resultingTM = new TextMessage(); resultingTM.PhoneNumber = textMessage.PhoneNumber; resultingTM.Message = textMessage.Message; using ( //var content = new StringContent(postData) var content = new StringContent(JsonConvert.SerializeObject(textMessage), System.Text.Encoding.UTF8, "application/json") ) { try { client.DefaultRequestHeaders.Remove("Authorization"); } catch { /* not there? that's ok. just add them */ } client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); // post async, await response HttpResponseMessage response = await client.PostAsync(posturi, content); if (response.IsSuccessStatusCode) { resultingTM.SentResult = await response.Content.ReadAsStringAsync(); resultingTM.SendSuccessful = true; } else { resultingTM.SentResult = "Error: " + response.StatusCode + " " + response.ReasonPhrase + ". " + response.Content.ReadAsStringAsync().Result; resultingTM.SendSuccessful = false; if (response.StatusCode == HttpStatusCode.Unauthorized) { // token timed out? Try authenticating and doing this again. Authenticate(); using ( //var content = new StringContent(postData) var contentAgain = new StringContent(JsonConvert.SerializeObject(textMessage), System.Text.Encoding.UTF8, "application/json") ) { response = await client.PostAsync(posturi, contentAgain); if (response.IsSuccessStatusCode) { resultingTM.SentResult = await response.Content.ReadAsStringAsync(); resultingTM.SendSuccessful = true; } else { resultingTM.SentResult = "Error: " + response.StatusCode + " " + response.ReasonPhrase + ". " + response.Content.ReadAsStringAsync().Result; resultingTM.SendSuccessful = false; } } } } } // end of content return resultingTM; } // end of Send Text public static TextMessage SendTextAndWaitForResponse(string phonenumber, string message) { TextMessage textmessage = new TextMessage(); textmessage.PhoneNumber = phonenumber; textmessage.Message = message; return SendTextAndWaitForResponse(textmessage); } public static TextMessage SendTextAndWaitForResponse(TextMessage textMessage) { DSATextMessages tm = new DSATextMessages(); TextMessage resultingTM = null; if (!IsPhoneNumber(textMessage.PhoneNumber)) { resultingTM = new TextMessage(); resultingTM.PhoneNumber = textMessage.PhoneNumber; resultingTM.Message = textMessage.Message; resultingTM.SendSuccessful = false; resultingTM.SentResult = "Invalid phone number " + textMessage.PhoneNumber; return resultingTM; } //Task.Factory.StartNew(() => // this syntax uses a thread from the pool, less overhead //new Thread(() => // this syntax creates a separate thread than the pool, more overhead, but pool is preserved Task.Factory.StartNew(() => { //Make async web request try { resultingTM = tm.SendText(textMessage).Result; } catch (Exception ex) { resultingTM = new TextMessage(); resultingTM.PhoneNumber = textMessage.PhoneNumber; resultingTM.Message = textMessage.Message; resultingTM.SendSuccessful = false; resultingTM.SentResult = ex.InnerException.Message; } }); int count_tries = 0; while (resultingTM == null && count_tries < 60) // delay for up to a minute -> this allows the threading to pop back and forth between tasks { count_tries++; Thread.Sleep(1000); } if (resultingTM == null) { resultingTM = new TextMessage(); resultingTM.PhoneNumber = textMessage.PhoneNumber; resultingTM.Message = textMessage.Message; resultingTM.SendSuccessful = false; resultingTM.SentResult = "Timeout sending message"; } return resultingTM; } public static TextMessage SendTextToMultipleAndWaitForResponse(List<string> phonenumberlist, string message) { TextMessage textmessage = new TextMessage(); textmessage.PhoneNumber = String.Join(",", phonenumberlist); textmessage.Message = message; return SendTextToMultipleAndWaitForResponse(textmessage); } public static TextMessage SendTextToMultipleAndWaitForResponse(string phonenumberlist, string message) { TextMessage textmessage = new TextMessage(); textmessage.PhoneNumber = phonenumberlist; textmessage.Message = message; return SendTextToMultipleAndWaitForResponse(textmessage); } /// <summary> /// textMessage.PhoneNumber can have multiple comma delimited phone numbers /// </summary> /// <param name="textMessage"></param> /// <returns></returns> public static TextMessage SendTextToMultipleAndWaitForResponse(TextMessage textMessage) { DSATextMessages tm = new DSATextMessages(); TextMessage resultingTM = null; //Task.Factory.StartNew(() => // this syntax uses a thread from the pool, less overhead //new Thread(() => // this syntax creates a separate thread than the pool, more overhead, but pool is preserved Task.Factory.StartNew(() => { //Make async web request try { resultingTM = tm.SendText(textMessage).Result; } catch (Exception ex) { resultingTM = new TextMessage(); resultingTM.PhoneNumber = textMessage.PhoneNumber; resultingTM.Message = textMessage.Message; resultingTM.SendSuccessful = false; resultingTM.SentResult = ex.InnerException.Message; } }); int count_tries = 0; while (resultingTM == null && count_tries < 60) // delay for up to a minute -> this allows the threading to pop back and forth between tasks { count_tries++; Thread.Sleep(1000); } if (resultingTM == null) { resultingTM = new TextMessage(); resultingTM.PhoneNumber = textMessage.PhoneNumber; resultingTM.Message = textMessage.Message; resultingTM.SendSuccessful = false; resultingTM.SentResult = "Timeout sending message"; } return resultingTM; } public static bool IsPhoneNumber(string number) { long po = 0; return long.TryParse(number, out po); //return Regex.Match(number, @"^(\+[0-9]{9})$").Success; } public static void EmailWebTeam(string subject, string message) { Task.Factory.StartNew(() => { var body = "<p>{0}</p>"; MailMessage email = new MailMessage(); email.To.Add(new MailAddress(supportaddress)); email.From = new MailAddress(fromaddress); email.Subject = subject; email.Body = string.Format(body, message); email.IsBodyHtml = true; using (var smtp = new SmtpClient()) { if (smtpusername != null && smtpusername != "") { var credential = new NetworkCredential { UserName = smtpusername, Password = smtppassword }; smtp.Credentials = credential; } smtp.Host = smtpserver; smtp.Port = Convert.ToInt32(smtpport); smtp.EnableSsl = true; smtp.Send(email); } }); Thread.Sleep(10); } public class TextMessage { //public Guid ProgramId { get { return programguid; } set { throw new Exception("cannot set GUID"); } } public string Message { get; set; } private string _PhoneNumber = ""; public string PhoneNumber { get { return _PhoneNumber; } set { _PhoneNumber = value.Replace(" ", "").Replace("(", "").Replace(")", "").Replace("-", "").Replace(".", "").Replace("+", ""); } } public bool? SendSuccessful { get; set; } public string SentResult { get; set; } } } }
What is JWT?
- What is JWT?
- Can securely transfer info between any two points; eg, an API
- Work across any programming language
- They are self-contained
- It is compact, you can send via a post request as a header and can be passed around easily
- Fast transmission
- Avoids querying the db multiple times – the token is all that is required.
- A JWT consists of a header, payload, and a signature in the format of aaaaa.bbbbbb.cccccc
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- 2 parts, the type (JWT), and the hashing algorithm (HMAC)
- eyJ1bmlxdWVfbmFtZSI6IjZ5bVhVVFBmeU5HNnBCamQiLCJuYmYiOjE1NDA1NzQ5MTgsImV4cCI6MTU1NjI5OTcxOCwiaWF0IjoxNTQwNTc0OTE4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMTkxIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDE5MSJ9
- The claim or info you want to submit, such as user details
- PRpEJKAsAUeyqp8l5rqgxWPmnKT2x098eNjxu2X2Xtw
- The signature is made up of a hash of the header, the payload, and the secret
More information can be found at https://scotch.io/tutorials/the-anatomy-of-a-json-web-token