Jasmin 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 Jasmin /send 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 Jasmin alternative overview. For the cutover procedure, read the migration guide.

API compatibility

When you switch from Jasmin to Ozeki, you throw out Jasmin, and switch to the Jasmin 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 Jasmin apps (Figure 1).

Figure 1 - API compatibility

How the Jasmin HTTP API Works

Jasmin exposes message submission through its HTTP API. Applications send an HTTP GET or POST request to the /send path on the configured http-api port, which is 1401 in a default installation. The request carries the credentials of a Jasmin user account, the recipient, the message content, and optional parameters for routing, encoding and delivery reports. Jasmin answers with a plain text body and an HTTP 200 status code. On success the body starts with Success followed by a quoted message id, a UUID assigned to the queued message, for example Success "07033084-5cfd-4812-90a4-e4d24ffb6e3d". This simple contract, a single URL with query parameters, is the reason thousands of applications integrated with Jasmin 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 Jasmin today:

http://jasmin.example.com:1401/send?username=myuser&password=secret&to=%2B36201234567&from=MyApp&content=Hello+world

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

http://ozeki.example.com:PORT/send?username=myuser&password=secret&to=%2B36201234567&from=MyApp&content=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/send?username=myuser&password=secret&to=%2B36201234567&from=MyApp&content=Hello+world"

Success "07033084-5cfd-4812-90a4-e4d24ffb6e3d"

Ozeki returns the same plain text responses your code already parses: Success "uuid" with HTTP 200 when the message is accepted and queued, Error "No route found" with HTTP 412 when no route matches the message, and Error "Authentication failure for username:myuser" with HTTP 403 when the credentials do not match a user account. The complete set of status codes is listed in the table below.

HTTP CodeBodyMeaning
200Success "uuid"Message accepted and queued, message id returned
400Error "Mandatory arguments not found, please refer to the HTTPAPI specifications."Request validation error
400Error "Argument _ is unknown." / "Argument _ has an invalid value: _."Unknown or invalid parameter
403Error "Authentication failure for username:_"Authentication error
403Error "Authorization failed for username:_"Credentials validation error
403Error "Cannot charge submit_sm, check RouterPB log file for details"User charging (balance) error
412Error "No route found"Message routing error
500Error "Cannot send submit_sm, check SMPPClientManagerPB log file for details"Fallback error, see gateway log for details

/send 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 username and password identify a user account, and Jasmin users require sufficient credit on their account, so the same quota behaviour applies in Ozeki.

ParameterPurpose
username, passwordCredentials of the Jasmin user account
toRecipient phone number, URL encoded
fromSender address (sender ID or number)
contentMessage body, URL encoded
hex-contentBinary message content in hexadecimal, alternative to content
codingData coding scheme: 0 (SMSC default / GSM 7 bit), 2 (8 bit binary), 8 (UCS-2), ...
priorityMessage priority: 0 (lowest), 1, 2 or 3
sdtScheduled delivery time in SMPP absolute or relative format
validity-periodValidity period in minutes
dlrDelivery report request: yes or no
dlr-urlCallback URL that receives the delivery events
dlr-levelReport level: 1 (SMSC), 2 (terminal), 3 (both)
dlr-methodHTTP method of the callback: GET or POST
tagsMessage tags for routing and interception logic

Delivery Report Callbacks: dlr, dlr-url, dlr-level and dlr-method

Delivery reports are the part of the /send contract that applications depend on most. Three parameters control them. dlr=yes enables tracking for the message, dlr-url is the callback URL, dlr-level selects which events trigger a callback, and dlr-method selects whether the callback is an HTTP GET or POST request:

dlr-levelEvent
1SMSC level: the report is delivered when the SMSC acknowledges the submit
2Terminal level: the report is delivered when the phone acknowledges the message
3Both SMSC and terminal level reports are delivered

When an event occurs, the gateway calls dlr-url and appends the report parameters as name value pairs. The parameters below are always present:

ParameterReplaced with
idInternal gateway message id (UUID)
message_statusSMPP command status, e.g. ESME_ROK on delivery success
levelThe dlr-level originally requested
connectorThe SMPP connector that handled the message
id_smscMessage id returned by the SMSC (level 2 and 3)
subdate, donedateSubmission and final state times (level 2 and 3)
errNetwork or SMSC error code on failure (level 2 and 3)
textFirst 20 characters of the message (level 2 and 3)

If your application passes dlr-url=http://app.example.com/dlr, it receives requests like these:

GET /dlr?id=07033084-5cfd-4812-90a4-e4d24ffb6e3d&message_status=ESME_ROK&level=2&connector=myconn&text=Hello+world   (delivered)
GET /dlr?id=07033084-5cfd-4812-90a4-e4d24ffb6e3d&message_status=ESME_RINVNUMDESTS&level=2&connector=myconn   (failed)

One detail catches many developers. The receiving endpoint must acknowledge every callback by replying with HTTP 200 and a body containing exactly ACK/Jasmin. If the acknowledgement is missing, the DLRThrower service considers the delivery failed and retries the callback (by default up to 3 retries with a 30 second delay). Your existing Jasmin receiver already implements this, so it keeps working unchanged. Beyond HTTP callbacks, Ozeki also stores every message and its delivery status internally, which is covered on our Jasmin DLR alternative page.

Character Encoding, Long Messages and Binary Content

Both gateways handle message content the same way:

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

Text is submitted with the content parameter and binary content with hex-content, for example hex-content=0623063106460628 for a UCS-2 Arabic message. When your text contains characters the GSM alphabet cannot represent, submit it with coding=8, and the gateway converts the text to UCS-2.

Long messages are split into concatenated parts automatically. Jasmin splits by default using a 6 byte User Data Header (long_content_split=udh) and a maximum of 5 parts per message (long_content_max_parts=5); splitting through SAR options is also supported. The same limits and UDH values apply 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 Jasmin or Ozeki without modification. The only value to adjust is the base URL: point it at your Jasmin host and port today, and at your Ozeki host and port after the migration. The URL encoding functions take care of escaping the dlr-url value described above.

PHP

<?php
$baseUrl = "http://ozeki.example.com:PORT/send";

$query = http_build_query([
"username"   => "myuser",
"password"   => "secret",
"to"         => "+36201234567",
"from"       => "MyApp",
"content"    => "Hello from PHP",
"dlr"        => "yes",
"dlr-url"    => "http://app.example.com/dlr",
"dlr-level"  => "2",
"dlr-method" => "GET",
]);

$response = file_get_contents($baseUrl . "?" . $query);
echo $response; // Success "uuid"
?>

Python

import urllib.parse
import urllib.request

params = {
"username": "myuser",
"password": "secret",
"to": "+36201234567",
"from": "MyApp",
"content": "Hello from Python",
"dlr": "yes",
"dlr-url": "http://app.example.com/dlr",
"dlr-level": "2",
"dlr-method": "GET",
}

url = "http://ozeki.example.com:PORT/send?" + urllib.parse.urlencode(params)

with urllib.request.urlopen(url) as response:
    print(response.read().decode())  # Success "uuid"

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 url = "http://ozeki.example.com:PORT/send"
               + "?username=myuser&password=secret"
               + "&to=" + URLEncoder.encode("+36201234567", StandardCharsets.UTF_8)
               + "&from=MyApp"
               + "&content=" + URLEncoder.encode("Hello from Java", StandardCharsets.UTF_8)
               + "&dlr=yes&dlr-url=" + URLEncoder.encode("http://app.example.com/dlr", StandardCharsets.UTF_8)
               + "&dlr-level=2&dlr-method=GET";

    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 url = "http://ozeki.example.com:PORT/send"
            + "?username=myuser&password=secret"
            + "&to=" + Uri.EscapeDataString("+36201234567")
            + "&from=MyApp"
            + "&content=" + Uri.EscapeDataString("Hello from C#")
            + "&dlr=yes&dlr-url=" + Uri.EscapeDataString("http://app.example.com/dlr")
            + "&dlr-level=2&dlr-method=GET";

    using var client = new HttpClient();
    Console.WriteLine(client.GetStringAsync(url).Result);
    // Success "uuid"
}
}

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 /send path and parameters stay the same.
  • Responses. The plain text bodies, including the quoted message id, the route and authentication errors, and the HTTP status codes match the format your code already handles.
  • Routing. Jasmin's MT router and connector selection map to routing rules in Ozeki, as described in the migration guide.
  • Quotas. Jasmin users require sufficient credit, so user quotas behave the same way in Ozeki.
  • 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 Jasmin format indefinitely.

During evaluation, test the edge cases your production traffic includes: concatenated messages, UCS-2 text, binary content, and delivery report callbacks with the ACK/Jasmin acknowledgement. All of them can be verified with the free trial (download) before you plan the cutover. The complete reference, including the /balance, /rate and /metrics endpoints, is available on our Jasmin compatible HTTP API page.


More information