# Infobip

Send text messages to one or many recipients through the Infobip SMS API from an outgoing REST connection.

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=1791646777)

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=1791646780)

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 carry many messages ..
        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 turn such a response into an exception with the text.

## See also {#see-also}

- [Your first SMS](https://zato.io/docs/dev/examples/sms/send/tutorial.html) - One service that every other service sends text messages through
- [API keys](https://zato.io/docs/security/api-keys.html) - Every field of an API key definition, including the header it is sent in
- [Calling external REST APIs](https://zato.io/docs/dev/rest/calling-apis.html) - Parameters, headers and the response object of a REST call

## Learn more {#learn-more}

- [Development documentation](https://zato.io/docs/dev/) - Everything about writing services, in one place
- [Requests and responses](https://zato.io/docs/dev/request-response/) - What a service receives, what it returns and how to shape both
- [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
