# Twilio

Send text messages (SMS) with Twilio

![infobip.svg](https://zatosource-production.b-cdn.net/docs/gfx/dev/examples/sms/send/twilio/twilio.webp?v=1791902588)

This page will show you how to send text messages through Twilio from Python:

- What to collect from the Twilio console
- Which security definition and outgoing REST connection to create
- How to send a message from a service and read Twilio's response

## Collecting Twilio config {#collecting-twilio-config}

You'll need three details from the Twilio console:

- The Account SID, which is `AC` followed by 32 hexadecimal characters, in the Account Info section of the console's home page
- The auth token, in the same section
- The sender, which is a phone number of the account in E.164 form, such as `+12025550100`, or the SID of a Messaging Service, which begins with `MG`

## Creating Zato connections {#creating-zato-connections}

First, create a Basic Auth definition under `Security → Basic Auth` - Twilio signs in each request with the Account SID as the username and the auth token as the password:

| Field | Value |
| --- | --- |
| Name | `Twilio SMS` |
| Username | The Account SID |
| Password | The auth token |

![Basic Auth for Twilio](https://zatosource-production.b-cdn.net/docs/gfx/sms/send/twilio-basic-auth-create.webp?v=1792089158)

Dashboard menu: Security > Basic Auth

Next, create an outgoing REST connection under `Connections → Outgoing → REST`:

| Field | Value | Notes |
| --- | --- | --- |
| Name | `Twilio SMS` | --- |
| Host | `https://api.twilio.com` | --- |
| URL path | `/2010-04-01/Accounts/<Account SID>/Messages.json` | With the Account SID copied earlier |
| Data format | Form data | --- |
| Content type | `application/x-www-form-urlencoded` | --- |
| Security | `Basic Auth/Twilio SMS` | The definition just created |

![REST connection to Twilio](https://zatosource-production.b-cdn.net/docs/gfx/sms/send/twilio-rest-create.webp?v=1792089160)

Dashboard menu: Connections > Outgoing > REST

## Sending a message {#sending-a-message}

The service below will:

- Build the message from the input it received
- Send it to Twilio
- Return the ID Twilio gave the message and its status

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class SendTwilioSMS(Service):

    name = 'sms.twilio.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'],
        }
```

After invoking the service you'll see:

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

Twilio answers with `201 Created` and the message in the `queued` status, which means it has been accepted and is waiting to be passed to the carrier.

## Sending through a Messaging Service {#sending-through-a-messaging-service}

A Messaging Service holds a pool of the account's numbers and chooses the sending number for each message. To send through it, replace `From` with `MessagingServiceSid`:

```python
message = {
    'To': input.to,
    'MessagingServiceSid': 'MG2d8f4b6a1c3e5f7a9b0c2d4e6f8a1b3c',
    'Body': input.text,
}
```

## Errors {#errors}

When Twilio does not accept a message, it answers with a 4xx status and a JSON body whose `code` and `message` say why, e.g.:

```json
{
  "code": 21211,
  "message": "Invalid 'To' Phone Number: +1202555",
  "status": 400
}
```

For such a response, the service above raises an exception with the code and the message, and the code is described in Twilio's error dictionary.

## Test credentials {#test-credentials}

The Twilio console also has a test Account SID and a test auth token, under Account → API keys and tokens. Messages sent with them are accepted or rejected as with real credentials, but they are not delivered and they are not charged for.

To use them, create a second Basic Auth definition and REST connection with the test credentials, and send from `+15005550006`. The recipient number determines the outcome:

| Recipient | Outcome |
| --- | --- |
| `+15005550006` | Accepted, with a message ID returned |
| `+15005550001` | Rejected with error 21211, an invalid number |
| `+15005550009` | Rejected with error 21614, a number that cannot receive SMS |

## See also {#see-also}

- [How to send text messages (SMS)](https://zato.io/docs/dev/examples/sms/send/tutorial.html) - Send every text from one service, so a change of provider later means editing that service alone
- [Vonage](https://zato.io/docs/dev/examples/sms/send/vonage.html) - Send through Vonage with your organization's name as the sender, in countries that allow one
- [Infobip](https://zato.io/docs/dev/examples/sms/send/infobip.html) - Tell everyone at once - one request sends the same text to any number of recipients
- [Africa's Talking](https://zato.io/docs/dev/examples/sms/send/africas-talking.html) - Try every message for free in a sandbox that shows it on a simulated phone instead of delivering it
- [Basic Auth](https://zato.io/docs/security/basic-auth.html) - Keep the username and password out of your code - Zato adds them to every request the connection makes
- [Calling external REST APIs](https://zato.io/docs/dev/rest/calling-apis.html) - Call any other REST API the same way, with parameters, headers and status checks

## Learn more {#learn-more}

- [Development documentation](https://zato.io/docs/dev/) - The complete reference for writing services
- [Requests and responses](https://zato.io/docs/dev/request-response/) - What a service receives, what it returns and how both are structured
- [Integration examples](https://zato.io/docs/dev/examples/) - Ready-made code for the systems you are likely to connect to
- [IDE and debugging](https://zato.io/docs/dev/ide/) - Write services in the Dashboard or in your own editor
- [Data models](https://zato.io/docs/dev/model/) - Declare inputs and outputs and have them validated for you
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - The full platform tutorial, from installation to production patterns
