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:

ProviderSecurity definitionData format
TwilioBasic AuthForm data
VonageBasic AuthJSON
InfobipAPI keyJSON
Africa's TalkingAPI keyForm 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:

{"message_id": "SM9f2c5a1e8b7d4c3a2f1e0d9c8b7a6f5e", "status": "queued"}

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

PageWhat it covers
TwilioThe Twilio console details, the connection and a message sent through it
REST outgoing connectionsEvery field of an outgoing REST connection, timeouts and retries
Calling external REST APIsParameters, headers and the response object of a REST call

Learn more