REST v2

When to choose REST v2

Choose REST v2 for new SMS integrations, especially high-volume or asynchronous workflows, because it provides UUID-based tracking, modern request patterns, and required DLR callbacks for reliable delivery status handling; choose REST v1 only when you need compatibility with an existing v1 integration.

Overview

Our Mobile Gateway RESTv2 API allows you to send SMS messages.

The difference between REST and REST v2 is a more modern approach designed for scale and asynchronous messaging. DLR callback URLs are required in order to receive error codes.

HTTPS use is mandatory; all attempts to use plain-text HTTP will be redirected to HTTPS. Request and response data requires JSON encoding. Only HTTP GET and POST methods are required.

What the API can do

  • Send SMS messages to a single handset
  • Schedule SMS messages for future delivery
  • Track submitted messages with UUID message IDs
  • Retrieve submitted message details
  • Receive inbound MO message callbacks
  • Receive delivery receipt callbacks for message status updates

How it works

In practice, the API follows a simple workflow:

  1. Authenticate with your Mobile Gateway REST API v2 credentials.
  2. Submit a JSON request to send, schedule, or retrieve a message.
  3. Store the UUID message ID returned by the API.
  4. Use the UUID to retrieve message details or reconcile delivery receipt callbacks.
  5. Configure callback URLs so your application can receive inbound message and delivery status events.

When to use REST API v2

Use REST API v2 when your application needs a modern HTTPS SMS integration with asynchronous processing and UUID-based message tracking. It is a strong fit for new transactional messaging, operational alerts, scheduled sends, and high-throughput workflows where delivery outcomes need to be reconciled through callbacks.

REST API v2 should generally be preferred for new integrations. REST v1 is mainly useful when you are maintaining an existing integration that already depends on v1 endpoints, callbacks, or response formats.

Before you start

Before making your first request, confirm the following:

  1. You have Mobile Gateway REST API v2 credentials.
  2. Your DLR callback URL is configured for delivery status events.
  3. Your server IP address is authorised if IP allowlisting is enabled.
  4. Mobile numbers are formatted in international format, for example +64211234567.
  5. Your requests use HTTPS and JSON encoding.

First successful request path

For most implementations, the fastest way to validate connectivity and configuration is:

  1. Authenticate using your REST API v2 credentials.
  2. Submit a simple POST request to /messages with destination and content.
  3. Confirm the 202 Accepted response and store the returned UUID message ID.
  4. Retrieve the message with GET /messages?id={message_id}.

Authentication

HTTP Basic Authentication is used for all requests. If you access the API without having the correct credentials OR permission to access the API, you will get a HTTP 401 response.

Your gateway application credentials (‘application name’ and password) are available on the platform: https://indosatooredoo.modicagroup.com/gateway/api_config/restv2

You will need a login and password to retrieve these.

If you don’t already have these details, please contact support@modicagroup.com.

Authorised IP Addresses

You can whitelist your servers’ IPs or IP ranges using the ‘Add IP Address’ button under “Authorised IP Addresses”

Please note: once one or more IP addresses or IP ranges have been added connections from all other IP addresses will be rejected, any attempt from an IP not in the list will receive an Authentication error.

Base URI

All API access is over HTTPS, and accessed from:

https://api.modicagroup.com/rest/sms/v2

Versions

The REST API version is currently v2.

Accept: application/json

OpenAPI Specification

The OpenAPI (Swagger) specification can be found at here.

You can see code samples and a breakdown of the specification here.

Error Codes

The following errors can occur:

Code Description
send_failed Could not queue message due to an unknown error
invalid_json Invalid JSON data in the request body
missing_attrib Missing a required attribute
invalid_attrib Invalid attribute value
400 Invalid scheduled timestamp (must be RFC3339)
422 Invalid scheduled timestamp (must not be in the past)

Date Strings

Complete date plus hours, minutes, seconds & timezone.

YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
     YYYY = four-digit year
     MM   = two-digit month (01=January, etc.)
     DD   = two-digit day of month (01 through 31)
     hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
     mm   = two digits of minute (00 through 59)
     ss   = two digits of second (00 through 59)
     s    = one or more digits representing a decimal fraction of a second
     TZD  = time zone designator (Z or +hh:mm or -hh:mm)

Sending messages

Sending to a single destination

To send an MT message to a single mobile handset, submit a POST request:

POST /messages

{
  "destination": "+6412345678",
  "content": "Your SMS message content with a very long https://a.urltoshorten.com/thatislongerthanthemaximummessagelength?withparameters=likethisone embedded in the text"
}
infoThe number format must be international format e.g. +64211234567 / +61414123456 / +18123456789. International format usually means removing the leading zero (from local numbers) and replacing it with the country code preceded by a plus ( + ).
infoAs per the Base URI instructions above, all API access is over HTTPS and accessed from https://api.modicagroup.com/rest/sms/v2 – e.g. the full URI to send MT messages is https://api.modicagroup.com/rest/sms/v2/messages

Optional attributes:

{
  "scheduled": str: 2017-05-05T10:00:00+12:00,
  "source": str:short-code,
  "reference": str:alt-reference,
  "class": str:application-class,
  "mask": str:source-mask,
  "sms_class": int:0-3,
  "expires": str: 2017-05-05T10:00:00+12:00
}
infoThe class attribute defaults to mt_message
infoThe upper maximum for scheduled messages is 60 days

On success:

HTTP/1.1 202 Accepted
Content-Type: application/json

{"id":"1e41f423-24cf-4fa2-9a8a-888c30653305","status":"accepted","detail":"+6412345678"}
infoAll messages IDs will be returned as UUID.

On validation error:

HTTP/1.1 400 Bad Request

{
  "error": str:error-code
  "error-desc": [str:error-desc]
}
{
  "error-desc": "Invalid scheduled timestamp (must be less than 60 days)",
  "error": "invalid_attrib"
}

Send SMS Message Example

This example sends an SMS message using the REST API v2.

curl -v \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -u 'gw_username:abcde12345' \
  -d '{"destination": "+64211234567", "content": "Hello world!"}' \
  https://{apiDomainName}/rest/sms/v2/messages
const credentials = btoa("gw_username:abcde12345");

const response = await fetch("https://{apiDomainName}/rest/sms/v2/messages", {
  method: "POST",
  headers: {
    "Accept": "application/json",
    "Authorization": `Basic ${credentials}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    destination: "+64211234567",
    content: "Hello world!"
  })
});

const result = await response.json();
import requests
import json
from base64 import b64encode

uri = 'https://{apiDomainName}/rest/sms/v2/messages'
username = "gw_username"
password = "abcde12345"

# Authorization token
def basic_auth(username, password):
    token = b64encode(f"{username}:{password}".encode('utf-8')).decode("ascii")
    return f'Basic {token}'

headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization' : basic_auth(username, password)
}

json_payload = json.dumps({
  "destination": "+64211234567",
  "content": "Hello world!"
})

response = requests.post(uri, headers=headers, data=json_payload)

if response.status_code == 202:
    print(response.json())
else:
    print("Error:", response.status_code, response.text)
<?php
$payload = json_encode([
    "destination" => "+64211234567",
    "content" => "Hello world!"
]);

$ch = curl_init("https://{apiDomainName}/rest/sms/v2/messages");
curl_setopt($ch, CURLOPT_USERPWD, "gw_username:abcde12345");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Accept: application/json",
    "Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);
?>
require "json"
require "net/http"
require "uri"

uri = URI("https://{apiDomainName}/rest/sms/v2/messages")
request = Net::HTTP::Post.new(uri)
request.basic_auth("gw_username", "abcde12345")
request["Accept"] = "application/json"
request["Content-Type"] = "application/json"
request.body = {
  destination: "+64211234567",
  content: "Hello world!"
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

String token = Base64.getEncoder()
    .encodeToString("gw_username:abcde12345".getBytes(StandardCharsets.UTF_8));

String payload = """
{
  "destination": "+64211234567",
  "content": "Hello world!"
}
""";

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://{apiDomainName}/rest/sms/v2/messages"))
    .header("Accept", "application/json")
    .header("Authorization", "Basic " + token)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(payload))
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes("gw_username:abcde12345"));

var request = new HttpRequestMessage(
    HttpMethod.Post,
    "https://{apiDomainName}/rest/sms/v2/messages");
request.Headers.Accept.ParseAdd("application/json");
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);
request.Content = new StringContent(
    """{"destination":"+64211234567","content":"Hello world!"}""",
    Encoding.UTF8,
    "application/json");

var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();

Output:

{'id': '287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c', 'status': 'accepted', 'detail': '+64211234567'}

Retrieving an SMS Message

To retrieve a message submit a GET request:

GET /messages?id=[str:message-id]

If a match is found:

HTTP/1.1 200 OK
Location: https://api.modicagroup.com/rest/sms/v2/messages?id=[str:message-id]

{
  "id": str:message-id,
  "source": str:mobile-number|short-code,
  "destination": str:mobile-number|short-code,
  "content": str:text-message
  "status": str:status
}

Additional attributes are added if available:

{
  "reference": str:alt-reference,
}

If not found:

HTTP/1.1 404 Not Found

Get SMS Message Example

This example retrieves an SMS message using the REST API v2.

curl -v \
  -H 'Accept: application/json' \
  -u 'gw_username:abcde12345' \
  'https://{apiDomainName}/rest/sms/v2/messages?id=287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c'
const credentials = btoa("gw_username:abcde12345");

const response = await fetch("https://{apiDomainName}/rest/sms/v2/messages?id=287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c", {
  method: "GET",
  headers: {
    "Accept": "application/json",
    "Authorization": `Basic ${credentials}`
  }
});

const message = await response.json();
import requests
import json
from base64 import b64encode

uri = 'https://{apiDomainName}/rest/sms/v2/messages?id=287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c'
username = "gw_username"
password = "abcde12345"

# Authorization token
def basic_auth(username, password):
    token = b64encode(f"{username}:{password}".encode('utf-8')).decode("ascii")
    return f'Basic {token}'

headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization' : basic_auth(username, password)
}

response = requests.get(uri, headers=headers)

if response.status_code == 200:
    print(response.json())
else:
    print("Error:", response.status_code, response.text)
<?php
$ch = curl_init("https://{apiDomainName}/rest/sms/v2/messages?id=287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c");
curl_setopt($ch, CURLOPT_USERPWD, "gw_username:abcde12345");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);
?>
require "json"
require "net/http"
require "uri"

uri = URI("https://{apiDomainName}/rest/sms/v2/messages?id=287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c")
request = Net::HTTP::Get.new(uri)
request.basic_auth("gw_username", "abcde12345")
request["Accept"] = "application/json"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

message = JSON.parse(response.body)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

String token = Base64.getEncoder()
    .encodeToString("gw_username:abcde12345".getBytes(StandardCharsets.UTF_8));

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://{apiDomainName}/rest/sms/v2/messages?id=287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c"))
    .header("Accept", "application/json")
    .header("Authorization", "Basic " + token)
    .GET()
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes("gw_username:abcde12345"));

var request = new HttpRequestMessage(
    HttpMethod.Get,
    "https://{apiDomainName}/rest/sms/v2/messages?id=287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c");
request.Headers.Accept.ParseAdd("application/json");
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);

var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();

Output:

{'id': '287c3a15-c9a8-4a14-b6de-e7a3f06b9b0c', source: '202', destination: '+64211234567', 'reference': '', 'content': 'Hello world!', 'status': 'accepted'}

MO Callback

You will only receive MO messages if you have configured an MO callback URL within your API Configuration.

We recommend using https:// for your callback URLs.

infoImportant: If your callback URL includes authentication credentials, ensure all special characters are correctly URL encoded. For more information please visit https://www.w3schools.com/tags/ref_urlencode.asp
warningNOTE: The security certificate must match the domain name being used, self signed certificates will not verify and will generate errors.

When a MO message is received for you a POST request is made to the MO callback URL, this callback will include the MO message detail as a JSON object in the body of the POST request.

POST callback-url

{
  "id": str($uuid):message-id,
  "source": str:mobile-number,
  "destination": str:short-code,
  "content": str:text-message,
  "operator": str:operator-name
}
infoAll messages IDs will be returned as UUID.

In the case that the message is a reply to an MT message an additional “reply_to” attribute is added (only when a number sequence is used, and the MO is in reply to an MT message):

{
  "reply_to": str:message-id
}

If the message contains a reply_to and the MT message had a reference provided, then an additional parameter will be added:

{
  "reference": str:reference
}

If the message from the handset contains binary content, one additional attribute will be supplied:

{
  "encoding": str:encoding-type
}

The value “base64” will be supplied for messages containing binary data. Content will be supplied encoded using base64, decoding content is needed to obtain the original data. NOTE: Regular SMS messages with GSM 7-bit or Unicode content will not supply this parameter.

DLR Callback

You will only receive DLR Status messages if you have configured a DLR callback URL within your API Configuration.

We recommend using https:// for your callback URLs.

infoImportant: If your callback URL includes authentication credentials, ensure all special characters are correctly URL encoded. For more information please visit https://www.w3schools.com/tags/ref_urlencode.asp
warningNOTE: The security certificate must match the domain name being used, self signed certificates will not verify and will generate errors.

When a DLR message is received for you a POST request is made to the DLR callback URL, this callback will include the DLR Status detail as a JSON object in the body of the POST request.

POST callback-url

{
  "message_id": str($uuid):message-id,
  "status": str:dlr-status
  "detail": str:detail
}
infoAll messages IDs will be returned as UUID.

In the case that the MT message contained a reference, an additional “reference” attribute is added:

{
  "reference": str:alt-reference
}

DLR Message Status

The following are the status codes returned in DLRs that our message gateway supports.

Status Description
sent Message has been sent by the carrier transport
received Message has been received
rejected The carrier rejected the message
expired The carrier was unable to deliver the message in a specified amount of time. For instance when the phone was turned off.

Omni Message Status

The following are the status codes viewable within Omni reports that our message gateway supports, not all carriers will support all of them.

Status Description
submitted Message successfully submitted to the carrier for delivery
sent Message has been sent by the carrier transport
received Message has been received
frozen A transient error has frozen this message
rejected The carrier rejected the message
failed Message delivery has failed due to a carrier connectivity issue
dead Message killed by an administrator
expired The carrier was unable to deliver the message in a specified amount of time. For instance when the phone was turned off.