Kannel Compatible API: Keep Your Existing Code When You Switch to Ozeki

The most expensive part of replacing an SMS gateway is not the gateway itself. It is the application work: every script, service and integration that calls the old API has to be found, changed, tested and redeployed. Ozeki SMS Gateway removes this cost. It implements the Kannel sendsms HTTP API, so your applications keep sending requests with the same URL path, the same parameters and the same response handling they use today. This page documents the interface from a developer perspective: the request format, a full parameter reference, delivery report callbacks, encoding rules, and code samples in PHP, Python, Java and C# that run against both gateways without modification. For the reasons to switch, see our Kannel alternative overview. For the cutover procedure, read the migration guide.

API compatibility

When you switch from Kannel to Ozeki, you throw out Kannel, and switch to the Kannel compatible API built into Ozeki SMS Gateway. After migration your apps will connect to Ozeki. It is very crucial to understand the compatibility, and how Ozeki SMS Gateway provides compatible interfaces to your Kannel apps (Figure 1).

Figure 1 - API compatibility

How the Kannel sendsms HTTP API Works

Kannel exposes message submission through the smsbox HTTP interface. Applications send an HTTP GET or POST request to the /cgi-bin/sendsms path on the configured sendsms port, which is 13013 in a default installation. The request carries the credentials of a sendsms user, the recipient, the message text, and optional parameters for routing, encoding and delivery reports. Kannel answers with a plain text body and an HTTP 202 status code. This simple contract, a single URL with query parameters, is the reason thousands of applications integrated with Kannel over the years, and it is exactly the contract Ozeki reproduces.

The Same Request Works on Both Gateways

Suppose your application sends this request to Kannel today:

http://kannel.example.com:13013/cgi-bin/sendsms?username=myuser&password=secret&to=%2B36201234567&from=MyApp&text=Hello+world

To send the same message through Ozeki, change only the host and port:

http://ozeki.example.com:PORT/cgi-bin/sendsms?username=myuser&password=secret&to=%2B36201234567&from=MyApp&text=Hello+world

The path, the parameters and the values are identical. A quick test with curl confirms the response format:

curl "http://ozeki.example.com:PORT/cgi-bin/sendsms?username=myuser&password=secret&to=%2B36201234567&from=MyApp&text=Hello+world"

0: Accepted for delivery

Ozeki returns the same plain text responses your code already parses: 0: Accepted for delivery when the message is accepted, 3: Queued message accepted for delivery when it is queued, and Authorization failed when the credentials do not match a user account.

sendsms Parameter Reference

The table below lists the parameters your applications may use. Ozeki accepts each one with the same meaning, so no request has to be rewritten. Note that the smsc parameter, which forces a message onto a specific SMSC connection in Kannel, selects the corresponding connection or route in Ozeki.

ParameterPurpose
username, passwordCredentials of the sendsms user
toRecipient phone number, URL encoded
fromSender address (sender ID or number)
textMessage body, URL encoded
smscRoutes the message to a specific SMSC connection
udhUser data header in hexadecimal, for concatenated and binary messages
codingData coding: 0 (GSM 7 bit), 1 (8 bit binary), 2 (UCS-2)
charsetCharacter set of the text parameter, for example utf-8
dlrmaskBitmask selecting which delivery events trigger a callback
dlrurlCallback URL that receives the delivery events
mclassMessage class: 0 normal, 1 flash, 2 phone memory, 3 SIM card
flashShorthand for mclass=1
pidProtocol identifier, used for special message types
mwiMessage waiting indicator settings
alt-dcsAlternate data coding scheme handling
validityValidity period in minutes
deferredDeferred delivery time in minutes
accountAccount identifier passed along for billing
binfoBilling identifier

Delivery Report Callbacks: dlrmask and dlrurl

Delivery reports are the part of the sendsms contract that applications depend on most. Two parameters control them. dlrmask is a bitmask that selects which events trigger a callback:

BitEvent
1Delivered to the phone (delivery success)
2Not delivered (delivery failure)
4Buffered on the SMSC (queued for later delivery)
8Accepted by the SMSC (submit success)
16Rejected by the SMSC (submit failure)

Common values are dlrmask=3 for the final delivery result and dlrmask=31 for all events. dlrurl is the callback URL. When an event occurs, the gateway calls this URL and replaces substitution variables with the actual values:

VariableReplaced with
%dNumeric delivery status code matching the bits above
%sSender address
%rRecipient address
%tTime of the event
%iSMSC identifier of the connection that handled the message
%qMessage ID assigned by the SMSC

If your application passes dlrurl=http://app.example.com/dlr?id=123&status=%d, it receives requests like these:

GET /dlr?id=123&status=1   (delivered)
GET /dlr?id=123&status=2   (failed)

One detail catches many developers. Inside the sendsms request, the dlrurl value must be URL encoded, so the %d variable is transmitted as %25d. The request parameter therefore looks like this:

dlrurl=http%3A%2F%2Fapp.example.com%2Fdlr%3Fid%3D123%26status%3D%25d

If you use a URL encoding function in your code, as the samples below do, this happens automatically. Beyond HTTP callbacks, Ozeki also stores every message and its delivery status internally, which is covered on our Kannel DLR alternative page.

Character Encoding, Long Messages and Binary Content

Both gateways handle message content the same way:

  • coding=0: GSM 03.38 default alphabet, 7 bit, up to 160 characters per message
  • coding=1: 8 bit binary content
  • coding=2: UCS-2 encoding for characters outside the GSM alphabet, 70 characters per part

The charset parameter tells the gateway which character set your text value uses, for example utf-8, and the gateway converts it to the GSM 7 bit alphabet when possible. When your text contains characters the GSM alphabet cannot represent, submit it with coding=2 and charset=utf-8, and the gateway converts the text to UCS-2.

Long messages are sent as concatenated parts. Applications that assemble parts manually pass a udh parameter in hexadecimal, for example udh=050003A70301 for the first of three parts with reference A7. The same UDH values work on both gateways, and binary formats such as WAP push messages and port addressed application data are submitted the same way.

Code Samples That Work Unchanged

The following samples send a message and print the gateway response. Each one runs against Kannel or Ozeki without modification. The only value to adjust is the base URL: point it at your Kannel host and port today, and at your Ozeki host and port after the migration. The URL encoding functions take care of the dlrurl escaping described above.

PHP

<?php
$baseUrl = "http://ozeki.example.com:PORT/cgi-bin/sendsms";

$query = http_build_query([
"username" => "myuser",
"password" => "secret",
"to"       => "+36201234567",
"from"     => "MyApp",
"text"     => "Hello from PHP",
"dlrmask"  => "3",
"dlrurl"   => "http://app.example.com/dlr?id=123&status=%d",
]);

$response = file_get_contents($baseUrl . "?" . $query);
echo $response; // 0: Accepted for delivery
?>

Python

import urllib.parse
import urllib.request

params = {
"username": "myuser",
"password": "secret",
"to": "+36201234567",
"from": "MyApp",
"text": "Hello from Python",
"dlrmask": "3",
"dlrurl": "http://app.example.com/dlr?id=123&status=%d",
}

url = "http://ozeki.example.com:PORT/cgi-bin/sendsms?" + urllib.parse.urlencode(params)

with urllib.request.urlopen(url) as response:
print(response.read().decode())  # 0: Accepted for delivery

Java

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

public class SendSms {
public static void main(String[] args) throws Exception {
    String dlr = URLEncoder.encode("http://app.example.com/dlr?id=123&status=%d",
                                   StandardCharsets.UTF_8);
    String url = "http://ozeki.example.com:PORT/cgi-bin/sendsms"
               + "?username=myuser&password=secret"
               + "&to=" + URLEncoder.encode("+36201234567", StandardCharsets.UTF_8)
               + "&from=MyApp"
               + "&text=" + URLEncoder.encode("Hello from Java", StandardCharsets.UTF_8)
               + "&dlrmask=3&dlrurl=" + dlr;

    HttpURLConnection c = (HttpURLConnection) new URL(url).openConnection();
    try (InputStream in = c.getInputStream()) {
        System.out.println(new String(in.readAllBytes(), StandardCharsets.UTF_8));
    }
}
}

C#

using System;
using System.Net;
using System.Net.Http;

class SendSms
{
static void Main()
{
    var dlr = Uri.EscapeDataString("http://app.example.com/dlr?id=123&status=%d");
    var url = "http://ozeki.example.com:PORT/cgi-bin/sendsms"
            + "?username=myuser&password=secret"
            + "&to=" + Uri.EscapeDataString("+36201234567")
            + "&from=MyApp"
            + "&text=" + Uri.EscapeDataString("Hello from C#")
            + "&dlrmask=3&dlrurl=" + dlr;

    using var client = new HttpClient();
    Console.WriteLine(client.GetStringAsync(url).Result);
    // 0: Accepted for delivery
}
}

Differences and Optional Ozeki Extensions

The compatibility is deliberate and complete on the application facing side, but a few points are worth knowing before you switch:

  • Base URL. The host and port change, the path and parameters stay the same.
  • Responses. The plain text bodies, including the queued and authorization failure messages, match the format your code already handles.
  • Routing. The smsc parameter maps to connection and route selection in Ozeki, as described in the migration guide.
  • Extensions. Ozeki additionally offers its native HTTP API, SQL database messaging, an SMPP server, and a searchable message archive with delivery status. These are optional. Nothing forces you to adopt them during the migration, and your applications can keep using the Kannel format indefinitely.

During evaluation, test the edge cases your production traffic includes: concatenated messages, UTF-8 text, binary content, and delivery report callbacks. All of them can be verified with the free trial before you plan the cutover.


More information