Your first SMS
Create the connection, send a text message from a service and let other services send through it.
This tutorial will show you how to send a text message from a Python service.
Every SMS provider offers an HTTP API for sending messages, so Zato reaches a provider through a regular outgoing REST connection:
- The 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 is ready the moment you click OK, with no restarts anywhere.
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 | The Twilio console details, the connection and a message sent through it |
| REST outgoing connections | Every field of an outgoing REST connection, timeouts and retries |
| Calling external REST APIs | Parameters, headers and the response object of a REST call |