Send SMS text messages using Go & Twillio
There are a lot of service providers which offer SMS sending capabilities. In this example we’re looking at using Twillio which has been around for a long time and is very established. Besides sending SMS text messages they also support voice and fax services and the ability to send push notifications to a mobile application. Sign up for a free Twillio account and register a phone number. They have numbers for many geographical locations, so you should be able to find a number local to your business.
The Twillio service provides a simply rest API for sending SMS text messages. In this tutorial we’ll write a Go function which interacts with the API using a web client. You’ll learn how to create a function called sendSMS which accepts two parameters. The first parameter will accept the mobile phone number to send the message too. The second parameter will be the actual message you want to send.
func main() {
err := sendSMS(
"+447934006066",
"The application server is down!")
if err != nil {
fmt.Println("Couldn't send the text message!")
fmt.Println(err.Error())
}
}
First you’ll need to create variables for your Twillio account ID and the authentication token which goes with the account. These are obtained from the Twillio web site for your account. The twillioAPI variable will hold the API endpoint that our program will communicate with. This URL is based on your Twillio account.
var (
twillioAccount = "XXXXXX"
twillioAuthToken = "YYYYYY"
twillioAPI = "https://api.twilio.com/2010-04-01/Accounts/"
+ twillioAccount + "/Messages.json"
)
Let’s start by defining our sendSMS Go function. The function will return any error encountered within it so that the calling block of code can handle the error. As you can see the function accepts two string variables for the mobile number and message. The variable apiValues is used to set the To and Body values passed into the function and we’ve hard coded the mobile number we are sending the message from. This will be the number that you register with Twillio. We then setup a strings Reader called apiValuesReader to read these values and pass them onto the web client.
func sendSMS(mobileNumber, message string) error {
var err error
var apiValues url.Values
var webClient *http.Client
var webRequest *http.Request
var webResponse *http.Response
// define a web request to the Twillio API
// endpoint passing in the prepared values
apiValues = url.Values{}
apiValues.Set("To", mobileNumber)
apiValues.Set("From", "+44122663032")
apiValues.Set("Body", message)
apiValuesReader := *strings.NewReader(apiValues.Encode())
}
The next step within the process is to create a webRequest object which uses the HTTP POST method on the Twillio API endpoint, passing in the values string reader. We need to check if creating this new request produced an error. If it has then we return the err and exit the function. If the request was created successfully, then we can set the HTTP authentication using our Twillio account ID and authentication token. We also need to set the appropriate HTTP headers for Accept and Content-Type
// define a web request to the Twillio API
// endpoint passing in the prepared values
webRequest, err = http.NewRequest("POST", twillioAPI, &apiValuesReader)
if err != nil {
return err
}
// define the HTTP basic authentication values
// and set two HTTP headers for Accept & Content Type
webRequest.SetBasicAuth(twillioAccount, twillioAuthToken)
webRequest.Header.Add("Accept", "application/json")
webRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded")
Now that the web request is setup we can now submit the request to the Twillio service. To do this we define a new http.Client object, setting a timeout of ten seconds. We do this to prevent having to wait for the standard Go timeout if the Twillio site is off-line. Again, we check the error status of web client call and return the error if one is encountered.
// create a web client with a
// timeout & submit the request
webClient = &http.Client{
Timeout: 10 * time.Second,
}
webResponse, err = webClient.Do(webRequest)
if err != nil {
return err
}
Finally we check the status code of the web client call to make sure the Twillio API has returned either a 200 or a 300 status code. If it has’nt we create a new error and return the actual status code returned.
If the API returns a valid HTTP status code, then we attempt to parse the returned JSON content. If Twillio successfully sent the SMS message then the returned JSON will contain an attribute called sid which is the messages unique sending ID produced by Twillio. In the example shown below we’re decoding the JSON and printing out this message ID if found within the response.
// check the response from the Twillio API
if webResponse.StatusCode >= 200 &&
webResponse.StatusCode < 300 {
// parse the returned data
// & display the message ID
var responseData map[string]interface{}
decoder := json.NewDecoder(webResponse.Body)
err := decoder.Decode(&responseData)
if err == nil {
fmt.Println("Message sent with ID")
fmt.Println(responseData["sid"])
return nil
}
return err
}
return errors.New(webResponse.Status)
The complete code
package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
)
var (
twillioAccount = "XXXXX"
twillioAuthToken = "YYYYY"
twillioAPI = "https://api.twilio.com/2010-04-01/Accounts/"
+ twillioAccount + "/Messages.json"
)
func main() {
err := sendSMS(
"+447934006066",
"The application server is down!")
if err != nil {
fmt.Println("Couldn't send the text message!")
fmt.Println(err.Error())
}
}
func sendSMS(mobileNumber, message string) error {
var err error
var apiValues url.Values
var webClient *http.Client
var webRequest *http.Request
var webResponse *http.Response
apiValues = url.Values{}
apiValues.Set("To", mobileNumber)
apiValues.Set("From", "+441228830322")
apiValues.Set("Body", message)
apiValuesReader := *strings.NewReader(apiValues.Encode())
webRequest, err = http.NewRequest("POST", twillioAPI, &apiValuesReader)
if err != nil {
return err
}
webRequest.SetBasicAuth(twillioAccount, twillioAuthToken)
webRequest.Header.Add("Accept", "application/json")
webRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded")
webClient = &http.Client{
Timeout: 10 * time.Second,
}
webResponse, err = webClient.Do(webRequest)
if err != nil {
return err
}
if webResponse.StatusCode >= 200 &&
webResponse.StatusCode < 300 {
var responseData map[string]interface{}
decoder := json.NewDecoder(webResponse.Body)
err := decoder.Decode(&responseData)
if err == nil {
fmt.Println("Message sent with ID")
fmt.Println(responseData["sid"])
return nil
}
return err
}
return errors.New(webResponse.Status)
}