# Infobip

Send text messages (SMS) with Infobip

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

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

- What to collect from the Infobip portal
- Which security definition and outgoing REST connection to create
- How to send a message from a service and read Infobip's response
- How to send the same message to many recipients in one request

## Collecting Infobip config {#collecting-infobip-config}

You'll need three details from the Infobip portal:

- The API base URL, which is specific to each account and is shown on the portal's home page, e.g. `xy1z2w.api.infobip.com`
- An API key, created on the API keys page of the portal
- The sender, which is a number or an alphanumeric sender ID registered with the account

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

First, create an API key definition under `Security → API keys`. Infobip expects the key in the `Authorization` header, after the word `App` and a space:

| Field | Value | Notes |
| --- | --- | --- |
| Name | `Infobip SMS` | --- |
| Header | `Authorization` | --- |
| API key | `App <API key>` | E.g. `App 4a3f6e1c...`, with the space |

![API key for Infobip](https://zatosource-production.b-cdn.net/docs/gfx/sms/send/infobip-apikey-create.webp?v=1792089153)

Dashboard menu: Security > API keys

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

| Field | Value | Notes |
| --- | --- | --- |
| Name | `Infobip SMS` | --- |
| Host | `https://<API base URL>` | E.g. `https://xy1z2w.api.infobip.com` |
| URL path | `/sms/3/messages` | --- |
| Data format | JSON | --- |
| Security | `API key/Infobip SMS` | The definition just created |

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

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 Infobip
- Return the ID Infobip gave the message and its status

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

# Zato
from zato.server.service import Service

class SendInfobipSMS(Service):

    name = 'sms.infobip.send'
    input = 'to', 'text'

    def handle(self):

        input = self.request.input

        # The message in Infobip's format ..
        message = {
            'sender': 'Riverside',
            'destinations': [{'to': input.to}],
            'content': {'text': input.text},
        }

        # .. one request can send many messages at once ..
        request = {'messages': [message]}

        # .. get the connection to Infobip ..
        conn = self.rest['Infobip SMS']

        # .. send the message ..
        response = conn.post(request)

        # .. make sure Infobip accepted it ..
        if not response.ok:
            request_error = response.data['requestError']
            error = request_error['serviceException']
            reason = error['text']
            raise Exception(f'Infobip error: {reason}')

        # .. there is one result for each message ..
        messages = response.data['messages']
        result = messages[0]
        status = result['status']

        # .. and tell our caller what Infobip said.
        self.response.payload = {
            'message_id': result['messageId'],
            'status': status['groupName'],
        }
```

Numbers go to Infobip in E.164 form without the leading `+`, so the service is invoked with `{"to": "12025550123", "text": "Your appointment is tomorrow at 9:00"}`.

After invoking the service you'll see:

```json
{"message_id": "4f7a2c9e1b3d5f6a8c0e", "status": "PENDING"}
```

The `PENDING` status group means Infobip has accepted the message and is delivering it.

## Sending to many recipients {#sending-to-many-recipients}

Say the clinic closes early one afternoon and every patient booked for that afternoon is to be told. A message can list any number of destinations, so one request sends the same text to all of them.

The service below will:

- Build one destination for each number in its input
- Send the message to all of them in one request
- Return the ID and the status of the message to each number

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

# Zato
from zato.server.service import Service

class SendInfobipSMSToMany(Service):

    name = 'sms.infobip.send-to-many'
    input = 'phones', 'text'

    def handle(self):

        input = self.request.input

        # One destination for each number ..
        destinations = []
        for phone in input.phones:
            destinations.append({'to': phone})

        # .. the message with all of them ..
        message = {
            'sender': 'Riverside',
            'destinations': destinations,
            'content': {'text': input.text},
        }

        # .. send it in one request ..
        request = {'messages': [message]}
        conn = self.rest['Infobip SMS']
        response = conn.post(request)

        if not response.ok:
            request_error = response.data['requestError']
            error = request_error['serviceException']
            reason = error['text']
            raise Exception(f'Infobip error: {reason}')

        # .. Infobip returns one result for each destination ..
        results = []
        for item in response.data['messages']:
            status = item['status']
            result = {
                'phone': item['destination'],
                'message_id': item['messageId'],
                'status': status['groupName'],
            }
            results.append(result)

        # .. and our caller receives all of them.
        self.response.payload = results
```

After invoking the service you'll see:

```json
[
  {
    "phone": "12025550123",
    "message_id": "4f7a2c9e1b3d5f6a8c0e",
    "status": "PENDING"
  },
  {
    "phone": "12025550124",
    "message_id": "8b1d3f5a7c9e2b4d6f8a",
    "status": "PENDING"
  }
]
```

## Errors {#errors}

When Infobip does not accept a request, it answers with a 4xx status and a JSON body whose `requestError.serviceException.text` says why, e.g.:

```json
{
  "requestError": {
    "serviceException": {
      "messageId": "UNAUTHORIZED",
      "text": "Invalid login details"
    }
  }
}
```

The services above raise an exception with the text of such a response.

## 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
- [Twilio](https://zato.io/docs/dev/examples/sms/send/twilio.html) - Send through Twilio and test for free with credentials that never deliver or charge for a message
- [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
- [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
- [API keys](https://zato.io/docs/security/api-keys.html) - Keep the API key out of your code - Zato adds it 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
