How to send text messages (SMS)
Create SMS connections and send text messages from Zato services

There are several popular SMS providers you can choose from for sending messages, and you communicate with each of them
using REST connections, where:
- A connection holds the provider's address and the path of its endpoint for sending messages
- A security definition holds the credentials, and the connection attaches them to each request
- Your service builds the message in the provider's format and posts it through the connection
Step 1 - Choose a provider
Each provider has its own page with the details to collect from its console, the security definition and the connection to create:
| Provider | Security definition | Data format |
|---|---|---|
| Twilio | Basic Auth | Form data |
| Vonage | Basic Auth | JSON |
| Infobip | API key | JSON |
| Africa's Talking | API key | Form data |
This tutorial uses Twilio.
Step 2 - Create the connection
Create the security definition and the outgoing REST connection described on the Twilio page, with the connection named Twilio SMS.
The connection can be used as soon as you click OK, and no restart is required.
Step 3 - Write the service
Say a clinic sends its patients text messages - reminders, changes to appointments and results ready for pick-up - and several services are to send them. The service below is the one place they all send from:
- It takes the recipient's number and the text of the message
- It builds the message in Twilio's format and sends it
- It returns the ID Twilio gave the message and its status
Take the code below and deploy it, then invoke it from the Dashboard's IDE under Services → IDE with {"to": "+12025550123", "text": "Your appointment is tomorrow at 9:00"} on input.
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class SendSMS(Service):
name = 'sms.send'
input = 'to', 'text'
def handle(self):
input = self.request.input
# The message in Twilio's format ..
message = {
'To': input.to,
'From': '+12025550100',
'Body': input.text,
}
# .. get the connection to Twilio ..
conn = self.rest['Twilio SMS']
# .. send the message ..
response = conn.post(message)
# .. make sure Twilio accepted it ..
if not response.ok:
code = response.data['code']
reason = response.data['message']
raise Exception(f'Twilio error {code}: {reason}')
# .. and tell our caller what Twilio said.
self.response.payload = {
'message_id': response.data['sid'],
'status': response.data['status'],
}
Step 4 - Run it
After invoking the service you'll see the ID of the message and its status as below:
The message reaches the phone within seconds, and the audit log of the connection records the request and Twilio's response.
Sending from other services
Other services do not need to know which provider sends their messages, or in which format. They invoke sms.send with a number and a text.
The service below sends a reminder for an appointment:
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class SendAppointmentReminder(Service):
name = 'clinic.send-appointment-reminder'
input = 'phone', 'location', 'starts_at'
def handle(self):
input = self.request.input
# The text of the reminder ..
text = f'Your visit at {input.location} is on {input.starts_at}.'
# .. the request to the service that sends text messages ..
request = {'to': input.phone, 'text': text}
# .. and send the reminder through it.
response = self.invoke('sms.send', request)
self.response.payload = response
A scheduled job can invoke this service each morning for the next day's appointments, and a REST channel can invoke it for the appointments booked online.
Changing providers
Because every service sends through sms.send, a change of provider is a change to that one service and to the connection it uses, and no other service changes.
For instance, with a Vonage connection named Vonage SMS, the message and the call become:
# The message in Vonage's format ..
message = {
'message_type': 'text',
'channel': 'sms',
'to': input.to.lstrip('+'),
'from': '12025550100',
'text': input.text,
}
# .. sent through the connection to Vonage.
conn = self.rest['Vonage SMS']
response = conn.post(message)
Phone numbers
All four providers expect numbers in the international E.164 form, which is a country code followed by the number, e.g. +12025550123. Twilio and Africa's Talking take the number with its leading +, while Vonage and Infobip take it without one, e.g. 12025550123.
See also
| Page | What it covers |
|---|---|
| Twilio | Send through Twilio and test for free with credentials that never deliver or charge for a message |
| Vonage | Send through Vonage with your organization's name as the sender, in countries that allow one |
| Infobip | Tell everyone at once - one request sends the same text to any number of recipients |
| Africa's Talking | Try every message for free in a sandbox that shows it on a simulated phone instead of delivering it |
| REST outgoing connections | Make sure no message is lost, with timeouts, retries and a queue for messages the provider could not take |
| Calling external REST APIs | Call any other REST API the same way, with parameters, headers and status checks |