Infobip

Send text messages (SMS) with Infobip

infobip.svg

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

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

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:

FieldValueNotes
NameInfobip SMS---
HeaderAuthorization---
API keyApp <API key>E.g. App 4a3f6e1c..., with the space
API key for Infobip

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

FieldValueNotes
NameInfobip SMS---
Hosthttps://<API base URL>E.g. https://xy1z2w.api.infobip.com
URL path/sms/3/messages---
Data formatJSON---
SecurityAPI key/Infobip SMSThe definition just created
REST connection to Infobip

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
# -*- 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:

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

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

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
# -*- 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:

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

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.:

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

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

See also

PageWhat it covers
How to send text messages (SMS)Send every text from one service, so a change of provider later means editing that service alone
TwilioSend through Twilio and test for free with credentials that never deliver or charge for a message
VonageSend through Vonage with your organization's name as the sender, in countries that allow one
Africa's TalkingTry every message for free in a sandbox that shows it on a simulated phone instead of delivering it
API keysKeep the API key out of your code - Zato adds it to every request the connection makes
Calling external REST APIsCall any other REST API the same way, with parameters, headers and status checks

Learn more