The base URL for the API is https://api.mobilemessage.com.au/. This documentation will help you understand how to send and receive SMS messages, set up webhooks for real-time notifications, and track message delivery statuses.
The Mobile Message API allows up to 5 simultaneous requests per account. If you exceed this limit, you'll receive an HTTP 429 error with the message: "Too many concurrent requests. Please wait." Simply wait for an existing request to complete before trying again.
OpenAPI Specification
A complete OpenAPI 3.0 specification of the v1 API is available at /assets/openapi.json. Use it to generate typed clients, import the API into tools like Postman or Insomnia, or power your own request validation.
Throughput
Batches are accepted at up to 10,000 messages per request with 5 concurrent requests. Accepted messages are queued and submitted to Australian carriers at a sustained rate of over 400 messages per second — a 50,000-message campaign is typically fully submitted to carriers within about two minutes. There is no per-second cap on API submissions; HTTP 429 is only returned for more than 5 concurrent requests.
Authentication
Use Basic Authentication with your API username and password to access the endpoints. Follow these steps:
- Combine your
username:password. - Encode the resulting string in Base64.
- Add this string to the
Authorizationheader as:Authorization: Basic {base64_encoded_credentials}.
Multiple API Keys and Rotation
Your account can hold multiple active API keys at the same time, managed in your dashboard under Settings > API. To rotate a credential with zero downtime: create a new key, update your application to use it, confirm traffic has switched over on the API logs page in your dashboard, then delete the old key. Each key is independent, so the old key keeps working until the moment you delete it.
Code Examples
curl -u user123:mypassword -X GET https://api.mobilemessage.com.au/v1/messages
import requests
from requests.auth import HTTPBasicAuth
response = requests.get('https://api.mobilemessage.com.au/v1/messages',
auth=HTTPBasicAuth('user123', 'mypassword'))
print(response.json())
const username = 'user123';
const password = 'mypassword';
fetch('https://api.mobilemessage.com.au/v1/messages', {
headers: {
'Authorization': 'Basic ' + btoa(`${username}:${password}`)
}
})
.then(response => response.json())
.then(data => console.log(data));
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/messages');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class APIRequest {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/messages");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Authorization", "Basic " + credentials);
connection.setRequestMethod("GET");
// Process the response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var byteArray = Encoding.ASCII.GetBytes("user123:mypassword");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var response = await client.GetAsync("https://api.mobilemessage.com.au/v1/messages");
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
Send SMS Messages
POST /v1/messages
This endpoint allows you to send one or more SMS messages. You can include up to 10,000 messages in a single request.
For automated integrations — particularly those sending large batches or implementing retry logic — we recommend including an Idempotency-Key header so that retries after a network error or timeout don't risk duplicate sends. Replayed requests return the original response with an Idempotency-Replay: true header; reusing a key with a different request payload returns HTTP 422. Keys are scoped to the API key that sent them and are kept for 24 hours. See Safely retrying API requests with an Idempotency-Key for details.
Top-level parameters
| Parameter | Type | Description |
|---|---|---|
messages |
Array of Objects | One or more message objects. |
enable_unicode (optional) |
Boolean | When true, messages that require UCS-2 (for example emojis or non-GSM characters) are sent using UCS-2. Defaults to false. Each message can still set unicode individually. |
max_parts (optional) |
Integer | Maximum SMS parts (credits) per message, applied to all messages in the batch. Messages exceeding this limit are skipped with status error. Default 10, range 1–99. |
ignore_unsubscribes (optional) |
Boolean |
Set to true to bypass unsubscribe blocking for this send request.
If omitted or set to false, normal unsubscribe blocking applies.
Use with caution, as bypassing your unsubscribe list could result in spam complaints.
|
Message object fields
| Field | Type | Description |
|---|---|---|
to |
String | The recipient's phone number, which can be in local Australian format or international format. |
message |
String |
The message content. Up to 10 parts are supported: • GSM-7: up to 1530 characters (10 × 153) • UCS-2: up to 670 characters (10 × 67) If Unicode is not enabled, non-GSM characters are stripped. Supports placeholders: {first_name}, {last_name}, {company}, {other}, {field_1} through {field_5}, and {optout}. The contact placeholders are filled from the recipient's saved contact record (if one exists). See below for details.
|
sender |
String | The mobile number or business name the message will come from. Must be a Sender ID registered in your account — use GET /v1/senders to list your available senders. |
custom_ref (optional) |
String | A custom reference to help track the message. |
unicode (optional) |
Boolean | Overrides the top-level enable_unicode for this message. When true, the message will be sent using UCS-2 if required. |
scheduled_for (optional) |
String | UTC datetime to send this message (ISO 8601, e.g. 2026-04-01T09:00:00). Must be more than 1 minute in the future — values at or before that (including past datetimes) are sent immediately rather than rejected. If omitted, the message is sent immediately. Scheduled messages can be cancelled via DELETE /v1/messages. |
{first_name}, {last_name}, {company}, {other}, and {field_1} through {field_5} will be replaced with the values from that contact record. If you have named your custom fields in the app (e.g. "Date of Birth"), you can also use the slug form {date_of_birth}. If no matching contact is found, placeholders are replaced with an empty string.
{optout} token: Include {optout} anywhere in your message body and it will automatically be replaced with an opt-out instruction before sending. The replacement is always exactly 20 characters:
- Dedicated or shared number senders →
Opt out: Reply Stop - Alphanumeric or own-number senders →
OptOut mb.st/XXXXXX(whereXXXXXXis your account's unique 6-character opt-out code)
Code examples
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/messages \
-H "Content-Type: application/json" \
-d '{
"enable_unicode": true,
"messages": [
{
"to": "0412345678",
"message": "Hello, this is a test message",
"sender": "CompanyABC",
"custom_ref": "tracking001"
},
{
"to": "0412345679",
"message": "Hello 🌏 from Mobile Message",
"sender": "CompanyABC",
"custom_ref": "tracking002",
"unicode": true
}
]
}'
import requests
from requests.auth import HTTPBasicAuth
data = {
"enable_unicode": True,
"messages": [
{
"to": "0412345678",
"message": "Hello, this is a test message",
"sender": "CompanyABC",
"custom_ref": "tracking001"
},
{
"to": "0412345679",
"message": "Hello 🌏 from Mobile Message",
"sender": "CompanyABC",
"custom_ref": "tracking002",
"unicode": true
}
]
}
response = requests.post('https://api.mobilemessage.com.au/v1/messages',
json=data, auth=HTTPBasicAuth('user123', 'mypassword'))
print(response.json())
const body = {
enable_unicode: true,
messages: [
{
to: "0412345678",
message: "Hello, this is a test message",
sender: "CompanyABC",
custom_ref: "tracking001"
},
{
to: "0412345679",
message: "Hello 🌏 from Mobile Message",
sender: "CompanyABC",
custom_ref: "tracking002",
unicode: true
}
]
};
fetch('https://api.mobilemessage.com.au/v1/messages', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
})
.then(r => r.json())
.then(console.log);
<?php
$data = [
"enable_unicode" => true,
"messages" => [
[
"to" => "0412345678",
"message" => "Hello, this is a test message",
"sender" => "CompanyABC",
"custom_ref" => "tracking001"
],
[
"to" => "0412345679",
"message" => "Hello 🌏 from Mobile Message",
"sender" => "CompanyABC",
"custom_ref" => "tracking002",
"unicode" => true
]
]
];
$ch = curl_init('https://api.mobilemessage.com.au/v1/messages');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class SendSMS {
public static void main(String[] args) throws Exception {
String json = """
{
"enable_unicode": true,
"messages": [
{"to":"0412345678","message":"Hello, this is a test message","sender":"CompanyABC","custom_ref":"tracking001"},
{"to":"0412345679","message":"Hello 🌏 from Mobile Message","sender":"CompanyABC","custom_ref":"tracking002","unicode":true}
]
}""";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/messages");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
class Program {
static async Task Main() {
var body = new {
enable_unicode = true,
messages = new[] {
new { to = "0412345678", message = "Hello, this is a test message", sender = "CompanyABC", custom_ref = "tracking001" },
new { to = "0412345679", message = "Hello 🌏 from Mobile Message", sender = "CompanyABC", custom_ref = "tracking002", unicode = true }
}
};
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/messages", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
Response example
{
"status": "complete",
"send_id": 42,
"ignore_unsubscribes": false,
"total_cost": 2,
"results": [
{
"to": "0412345678",
"message": "Hello, this is a test message",
"sender": "CompanyABC",
"custom_ref": "tracking001",
"status": "success",
"cost": 1,
"message_id": "abcd1234-efgh-5678-ijkl-9876543210mn",
"encoding": "gsm7"
},
{
"to": "0412345679",
"message": "Hello 🌏 from Mobile Message",
"sender": "CompanyABC",
"custom_ref": "tracking002",
"status": "success",
"cost": 1,
"message_id": "abcd1234-efgh-5678-ijkl-9876543210xy",
"encoding": "ucs2"
}
]
}
encoding appears only if Unicode was enabled in the request (either top-level or per message). The top-level send_id is only present when the request contains 2 or more valid messages. Per-message status can also be "blocked" when the recipient has unsubscribed. Entries that are structurally invalid (not an object, or missing to, message, or sender) come back with only status, error, and item (the zero-based index in your request) — match those errors to your input by item, not by recipient.
Send to List
POST /v1/list-send
Send an SMS to all contacts in a list in a single request. Supports per-contact variable substitution, {optout}, and scheduled delivery. Unsubscribed numbers are automatically filtered unless ignore_unsubscribes is set.
Use GET /v1/messages?custom_ref=your-ref to check delivery status per recipient after sending.
| Field | Type | Description |
|---|---|---|
list_id | Integer | ID of the list to send to. |
sender | String | Your approved Sender ID. Use GET /v1/senders to list your available senders. |
message | String | Message content. Supports {first_name}, {last_name}, {company}, {other}, {field_1}–{field_5} (or custom name slugs), and {optout}. Each contact's details are substituted individually. |
enable_unicode (optional) | Boolean | When true, messages containing emojis or non-GSM characters are sent as UCS-2. Defaults to false (non-GSM characters are stripped). |
max_parts (optional) | Integer | Maximum SMS parts (credits) per message. Messages that exceed this limit are skipped and counted in total_skipped_too_long. Default 10, range 1–99. |
custom_ref (optional) | String | Reference stored against every message in this send. |
ignore_unsubscribes (optional) | Boolean | Bypass unsubscribe filtering. Default false. |
scheduled_for (optional) | String | UTC datetime to send (ISO 8601). Must be more than 1 minute in the future — values at or before that (including past datetimes) are sent immediately rather than rejected. If omitted, sends immediately. A scheduled list send can be cancelled in bulk by passing the custom_ref to DELETE /v1/messages. |
stagger_minutes (optional) | Integer | Spread sending over this many minutes. Messages are divided into batches and sent gradually over the period. Requires 50+ recipients — with fewer, the value is silently ignored and the send goes out at once. Can be combined with scheduled_for to stagger from a future time. |
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/list-send \
-H "Content-Type: application/json" \
-d '{
"list_id": 5,
"sender": "CompanyABC",
"message": "Hi {first_name}, our sale starts today.{optout}",
"custom_ref": "march-sale"
}'
import requests
from requests.auth import HTTPBasicAuth
data = {
"list_id": 5,
"sender": "CompanyABC",
"message": "Hi {first_name}, our sale starts today.{optout}",
"custom_ref": "march-sale"
}
response = requests.post('https://api.mobilemessage.com.au/v1/list-send',
json=data, auth=HTTPBasicAuth('user123', 'mypassword'))
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/list-send', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
list_id: 5,
sender: 'CompanyABC',
message: 'Hi {first_name}, our sale starts today.{optout}',
custom_ref: 'march-sale'
})
})
.then(r => r.json())
.then(console.log);
<?php
$data = [
"list_id" => 5,
"sender" => "CompanyABC",
"message" => "Hi {first_name}, our sale starts today.{optout}",
"custom_ref" => "march-sale"
];
$ch = curl_init('https://api.mobilemessage.com.au/v1/list-send');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class SendToList {
public static void main(String[] args) throws Exception {
String json = "{\"list_id\":5,\"sender\":\"CompanyABC\","
+ "\"message\":\"Hi {first_name}, our sale starts today.{optout}\","
+ "\"custom_ref\":\"march-sale\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/list-send");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = "{\"list_id\":5,\"sender\":\"CompanyABC\","
+ "\"message\":\"Hi {first_name}, our sale starts today.{optout}\","
+ "\"custom_ref\":\"march-sale\"}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/list-send", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"send_id": 42,
"list_id": 5,
"total_recipients": 150,
"total_blocked_unsubscribed": 3,
"total_skipped_too_long": 0,
"total_cost": 150,
"scheduled_for": null,
"stagger_minutes": null,
"send_status": "queued"
}
send_status is "queued" for immediate sends, "scheduled" for future sends, or "staggered" when a stagger was applied. stagger_minutes echoes the stagger period, or null when not staggered.
Message History
GET /v1/messages
Retrieve sent message history. You can look up a specific message by ID or custom reference, or browse paginated history with optional filters.
Lookup by ID or reference
| Parameter | Type | Description |
|---|---|---|
message_id |
String | The unique message ID (UUID) returned when the message was sent. |
custom_ref |
String | Your custom reference (exact match). Returns all messages with this reference. |
Paginated history
When neither message_id nor custom_ref is provided, returns paginated outbound message history.
| Parameter | Type | Description |
|---|---|---|
status (optional) |
String | Filter by status. One of: pending, scheduled, sent, delivered, failed, cancelled. |
from (optional) |
String | Filter messages sent on or after this date (YYYY-MM-DD, UTC). |
to (optional) |
String | Filter messages sent before this date (YYYY-MM-DD, UTC). |
limit (optional) |
Integer | Results per page. Default 50, max 200. |
offset (optional) |
Integer | Pagination offset. Default 0. |
Code Examples
curl -u user123:mypassword -X GET "https://api.mobilemessage.com.au/v1/messages?message_id=abcd1234-efgh-5678-ijkl-9876543210mn"
import requests
from requests.auth import HTTPBasicAuth
url = "https://api.mobilemessage.com.au/v1/messages?message_id=abcd1234-efgh-5678-ijkl-9876543210mn"
response = requests.get(url, auth=HTTPBasicAuth('user123', 'mypassword'))
print(response.json())
const messageId = "abcd1234-efgh-5678-ijkl-9876543210mn";
fetch(`https://api.mobilemessage.com.au/v1/messages?message_id=${messageId}`, {
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword')
}
})
.then(response => response.json())
.then(data => console.log(data));
<?php
$message_id = "abcd1234-efgh-5678-ijkl-9876543210mn";
$url = "https://api.mobilemessage.com.au/v1/messages?message_id=" . $message_id;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class LookupMessage {
public static void main(String[] args) throws Exception {
String message_id = "abcd1234-efgh-5678-ijkl-9876543210mn";
URL url = new URL("https://api.mobilemessage.com.au/v1/messages?message_id=" + message_id);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
connection.setRequestProperty("Authorization", "Basic " + credentials);
connection.setRequestMethod("GET");
// Process the response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
string message_id = "abcd1234-efgh-5678-ijkl-9876543210mn";
string url = $"https://api.mobilemessage.com.au/v1/messages?message_id={message_id}";
var client = new HttpClient();
var byteArray = System.Text.Encoding.ASCII.GetBytes("user123:mypassword");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var response = await client.GetAsync(url);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
Response — lookup by ID or reference
{
"status": "complete",
"results": [
{
"to": "61412345678",
"message": "Hello, this is message 1",
"sender": "CompanyABC",
"custom_ref": "tracking001",
"status": "delivered",
"cost": "1.00",
"message_id": "abcd1234-efgh-5678-ijkl-9876543210mn",
"requested_at": "2026-01-15 09:35:00",
"scheduled_for": null,
"send_id": 42
}
]
}
Note the lookup response uses different field names to the paginated history response below. cost is returned as a decimal string on GET endpoints, and send_id is only present when the message was part of a batch.
Response — paginated history
curl -u user123:mypassword "https://api.mobilemessage.com.au/v1/messages?status=delivered&from=2026-01-01&limit=5"
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/messages',
params={'status': 'delivered', 'from': '2026-01-01', 'limit': 5},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/messages?status=delivered&from=2026-01-01&limit=5', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$url = 'https://api.mobilemessage.com.au/v1/messages?status=delivered&from=2026-01-01&limit=5';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class MessageHistory {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/messages?status=delivered&from=2026-01-01&limit=5");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/messages?status=delivered&from=2026-01-01&limit=5");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"total": 120,
"limit": 5,
"offset": 0,
"results": [
{
"message_id": "abcd1234-efgh-5678-ijkl-9876543210mn",
"recipient_number": "61412345678",
"sender_id": "CompanyABC",
"message_content": "Hello!",
"status": "delivered",
"cost": "1.00",
"custom_ref": "tracking001",
"requested_at": "2026-01-15 09:35:00",
"scheduled_for": null,
"send_id": 42
}
]
}
Cancel Scheduled Message
DELETE /v1/messages
Cancel one or more scheduled messages and receive a full credit refund. Only messages with status=scheduled can be cancelled. Provide message_id, custom_ref, or send_id.
Request body
| Field | Type | Description |
|---|---|---|
message_id | String | The UUID of a single scheduled message to cancel. |
custom_ref | String | Cancel all scheduled messages with this custom reference (useful for cancelling a batch). |
send_id | Integer | Cancel all scheduled messages in a bulk send. The send_id is returned when sending to multiple recipients via POST. |
Cancel by message_id
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/messages \
-H "Content-Type: application/json" \
-d '{"message_id":"abcd1234-efgh-5678-ijkl-9876543210mn"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
'https://api.mobilemessage.com.au/v1/messages',
json={"message_id": "abcd1234-efgh-5678-ijkl-9876543210mn"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/messages', {
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ message_id: 'abcd1234-efgh-5678-ijkl-9876543210mn' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/messages');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["message_id" => "abcd1234-efgh-5678-ijkl-9876543210mn"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class CancelMessage {
public static void main(String[] args) throws Exception {
String json = "{\"message_id\":\"abcd1234-efgh-5678-ijkl-9876543210mn\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/messages");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("DELETE");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = "{\"message_id\":\"abcd1234-efgh-5678-ijkl-9876543210mn\"}";
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.mobilemessage.com.au/v1/messages") {
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "message_id": "abcd1234-efgh-5678-ijkl-9876543210mn", "cancelled": true, "credits_refunded": 1 }
Cancel by custom_ref
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/messages \
-H "Content-Type: application/json" \
-d '{"custom_ref":"march-campaign"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
'https://api.mobilemessage.com.au/v1/messages',
json={"custom_ref": "march-campaign"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/messages', {
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ custom_ref: 'march-campaign' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/messages');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["custom_ref" => "march-campaign"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class CancelByRef {
public static void main(String[] args) throws Exception {
String json = "{\"custom_ref\":\"march-campaign\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/messages");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("DELETE");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.mobilemessage.com.au/v1/messages") {
Content = new StringContent("{\"custom_ref\":\"march-campaign\"}", Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "custom_ref": "march-campaign", "cancelled_count": 3, "credits_refunded": 3 }
Cancel by send_id
When sending to multiple recipients, the POST response includes a send_id. Use it to cancel all scheduled messages in that send:
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/messages \
-H "Content-Type: application/json" \
-d '{"send_id":42}'
{ "status": "complete", "send_id": 42, "cancelled_count": 150, "credits_refunded": 150 }
Returns HTTP 404 if no matching scheduled message is found. When cancelling by message_id, HTTP 409 is returned if the message exists but is not in a scheduled state. When cancelling by send_id or custom_ref, HTTP 404 is returned when no scheduled messages remain for that reference, including when they have already been sent.
Contacts
Manage your contact list. Contacts can be added to lists and used as personalisation sources when sending SMS.
GET /v1/contacts
List contacts with optional filters. Multiple filters are combined with AND. Text fields use partial matching.
| Parameter | Type | Description |
|---|---|---|
number (optional) | String | Exact match by phone number (Australian local or international format). |
first_name (optional) | String | Partial match on first name. |
last_name (optional) | String | Partial match on last name. |
company (optional) | String | Partial match on company. |
other (optional) | String | Partial match on the other/custom field. |
field_1 – field_5 (optional) | String | Partial match on custom fields 1–5. Custom field names are configured in the app under Contacts > Manage Fields. |
limit (optional) | Integer | Results per page. Default 50, max 200. |
offset (optional) | Integer | Pagination offset. Default 0. |
curl -u user123:mypassword "https://api.mobilemessage.com.au/v1/contacts?limit=10"
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/contacts',
params={'limit': 10},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/contacts?limit=10', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/contacts?limit=10');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetContacts {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/contacts?limit=10");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/contacts?limit=10");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"total": 120,
"limit": 10,
"offset": 0,
"results": [
{ "contact_id": 42, "number": "61412345678", "first_name": "Jane", "last_name": "Smith", "company": "Acme", "other": "", "field_1": "", "field_2": "", "field_3": "", "field_4": "", "field_5": "" }
]
}
POST /v1/contacts
Add a new contact. If duplicate contacts are disabled on your account, adding a number that already exists returns HTTP 409.
| Field | Type | Description |
|---|---|---|
number | String | Phone number (required). |
first_name (optional) | String | First name — used in {first_name} variable substitution. |
last_name (optional) | String | Last name — used in {last_name} substitution. |
company (optional) | String | Company — used in {company} substitution. |
other (optional) | String | Custom field — used in {other} substitution. |
field_1 – field_5 (optional) | String | Custom contact fields 1–5. Used in {field_1}–{field_5} substitution (or custom name slugs configured in the app). |
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/contacts \
-H "Content-Type: application/json" \
-d '{"number":"0412345678","first_name":"Jane","last_name":"Smith","company":"Acme"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/contacts',
json={"number": "0412345678", "first_name": "Jane", "last_name": "Smith", "company": "Acme"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/contacts', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ number: '0412345678', first_name: 'Jane', last_name: 'Smith', company: 'Acme' })
})
.then(r => r.json())
.then(console.log);
<?php
$data = ["number" => "0412345678", "first_name" => "Jane", "last_name" => "Smith", "company" => "Acme"];
$ch = curl_init('https://api.mobilemessage.com.au/v1/contacts');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class AddContact {
public static void main(String[] args) throws Exception {
String json = "{\"number\":\"0412345678\",\"first_name\":\"Jane\",\"last_name\":\"Smith\",\"company\":\"Acme\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/contacts");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = "{\"number\":\"0412345678\",\"first_name\":\"Jane\",\"last_name\":\"Smith\",\"company\":\"Acme\"}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/contacts", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "contact_id": 42, "number": "61412345678" }
PATCH /v1/contacts
Update one or more fields on an existing contact. Only the fields you provide will be changed.
| Field | Type | Description |
|---|---|---|
contact_id | Integer | ID of the contact to update (required). |
number (optional) | String | New phone number. |
first_name (optional) | String | First name. |
last_name (optional) | String | Last name. |
company (optional) | String | Company. |
other (optional) | String | Custom field. |
field_1 – field_5 (optional) | String | Custom contact fields 1–5. |
curl -u user123:mypassword -X PATCH https://api.mobilemessage.com.au/v1/contacts \
-H "Content-Type: application/json" \
-d '{"contact_id":42,"first_name":"Jane","company":"Acme Corp"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.patch(
'https://api.mobilemessage.com.au/v1/contacts',
json={"contact_id": 42, "first_name": "Jane", "company": "Acme Corp"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/contacts', {
method: 'PATCH',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ contact_id: 42, first_name: 'Jane', company: 'Acme Corp' })
})
.then(r => r.json())
.then(console.log);
<?php
$data = ["contact_id" => 42, "first_name" => "Jane", "company" => "Acme Corp"];
$ch = curl_init('https://api.mobilemessage.com.au/v1/contacts');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class EditContact {
public static void main(String[] args) throws Exception {
String json = "{\"contact_id\":42,\"first_name\":\"Jane\",\"company\":\"Acme Corp\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/contacts");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("PATCH");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = "{\"contact_id\":42,\"first_name\":\"Jane\",\"company\":\"Acme Corp\"}";
var request = new HttpRequestMessage(new HttpMethod("PATCH"), "https://api.mobilemessage.com.au/v1/contacts") {
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "contact_id": 42 }
DELETE /v1/contacts
Remove a contact. The contact is also removed from all lists.
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/contacts \
-H "Content-Type: application/json" \
-d '{"contact_id":42}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
'https://api.mobilemessage.com.au/v1/contacts',
json={"contact_id": 42},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/contacts', {
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ contact_id: 42 })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/contacts');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["contact_id" => 42]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class DeleteContact {
public static void main(String[] args) throws Exception {
String json = "{\"contact_id\":42}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/contacts");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("DELETE");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.mobilemessage.com.au/v1/contacts") {
Content = new StringContent("{\"contact_id\":42}", Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "contact_id": 42, "removed": true }
Contact Lists
Organise contacts into named lists for use with Send to List.
GET /v1/lists
List all contact lists including their contact count.
curl -u user123:mypassword https://api.mobilemessage.com.au/v1/lists
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/lists',
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/lists', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/lists');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetLists {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/lists");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/lists");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"results": [
{ "list_id": 5, "name": "VIP Customers", "created_at": "2026-01-15 09:00:00", "contact_count": 150 }
]
}
POST /v1/lists
Create a contact list. If a list with the same name already exists, the existing list is returned rather than a duplicate being created.
| Field | Type | Description |
|---|---|---|
name | String | The name for the new list (required). |
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/lists \
-H "Content-Type: application/json" \
-d '{"name":"VIP Customers"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/lists',
json={"name": "VIP Customers"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/lists', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'VIP Customers' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/lists');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["name" => "VIP Customers"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class CreateList {
public static void main(String[] args) throws Exception {
String json = "{\"name\":\"VIP Customers\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/lists");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var content = new StringContent("{\"name\":\"VIP Customers\"}", Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/lists", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "list_id": 5, "name": "VIP Customers", "existing": false }
existing is true when a list with this name already existed and was returned instead of created.
PATCH /v1/lists
Rename a list. Note: submitting the list's current name unchanged returns HTTP 404 (the update matches no rows) — treat it as a no-op, not a missing list.
| Field | Type | Description |
|---|---|---|
list_id | Integer | ID of the list to rename (required). |
name | String | New name for the list (required). |
curl -u user123:mypassword -X PATCH https://api.mobilemessage.com.au/v1/lists \
-H "Content-Type: application/json" \
-d '{"list_id":5,"name":"VIP Customers 2026"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.patch(
'https://api.mobilemessage.com.au/v1/lists',
json={"list_id": 5, "name": "VIP Customers 2026"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/lists', {
method: 'PATCH',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ list_id: 5, name: 'VIP Customers 2026' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/lists');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["list_id" => 5, "name" => "VIP Customers 2026"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class RenameList {
public static void main(String[] args) throws Exception {
String json = "{\"list_id\":5,\"name\":\"VIP Customers 2026\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/lists");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("PATCH");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = "{\"list_id\":5,\"name\":\"VIP Customers 2026\"}";
var request = new HttpRequestMessage(new HttpMethod("PATCH"), "https://api.mobilemessage.com.au/v1/lists") {
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "list_id": 5, "name": "VIP Customers 2026" }
DELETE /v1/lists
Delete a list. By default only the list and its membership records are removed — contact records are kept. Set delete_contacts to true to also permanently delete all contact records that were members of the list.
| Field | Type | Description |
|---|---|---|
list_id | Integer | ID of the list to delete (required). |
delete_contacts (optional) | Boolean | If true, also permanently delete all contact records that were members of this list. Defaults to false. |
# Delete list only (keep contacts)
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/lists \
-H "Content-Type: application/json" \
-d '{"list_id":5}'
# Delete list and all its contacts
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/lists \
-H "Content-Type: application/json" \
-d '{"list_id":5,"delete_contacts":true}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
'https://api.mobilemessage.com.au/v1/lists',
json={"list_id": 5}, # add "delete_contacts": True to also delete contacts
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/lists', {
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ list_id: 5 }) // add delete_contacts: true to also delete contacts
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/lists');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["list_id" => 5]));
// Add "delete_contacts" => true to also delete contacts
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class DeleteList {
public static void main(String[] args) throws Exception {
String json = "{\"list_id\":5}"; // add ,"delete_contacts":true to also delete contacts
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/lists");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("DELETE");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
// Add \"delete_contacts\":true to also delete contacts
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.mobilemessage.com.au/v1/lists") {
Content = new StringContent("{\"list_id\":5}", Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "list_id": 5, "removed": true }
List Members
Add, remove, and view contacts within a specific list.
GET /v1/list-contacts
| Parameter | Type | Description |
|---|---|---|
list_id | Integer | ID of the list to query (required). |
limit (optional) | Integer | Default 50, max 200. |
offset (optional) | Integer | Pagination offset. |
curl -u user123:mypassword "https://api.mobilemessage.com.au/v1/list-contacts?list_id=5"
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/list-contacts',
params={'list_id': 5},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/list-contacts?list_id=5', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/list-contacts?list_id=5');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetListMembers {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/list-contacts?list_id=5");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/list-contacts?list_id=5");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"list_id": 5,
"total": 1,
"limit": 50,
"offset": 0,
"results": [
{ "contact_id": 42, "number": "61412345678", "first_name": "Jane", "last_name": "Smith", "company": "Acme", "other": "", "field_1": "", "field_2": "", "field_3": "", "field_4": "", "field_5": "", "added": "2026-01-20 10:00:00" }
]
}
POST /v1/list-contacts
Add a contact to a list. Has no effect if the contact is already a member.
| Field | Type | Description |
|---|---|---|
list_id | Integer | ID of the list (required). |
contact_id (optional*) | Integer | ID of the contact to add. One of contact_id or number is required. |
number (optional*) | String | Australian mobile number of the contact to add. One of contact_id or number is required. |
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/list-contacts \
-H "Content-Type: application/json" \
-d '{"list_id":5,"contact_id":42}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/list-contacts',
json={"list_id": 5, "contact_id": 42},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/list-contacts', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ list_id: 5, contact_id: 42 })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/list-contacts');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["list_id" => 5, "contact_id" => 42]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class AddListMember {
public static void main(String[] args) throws Exception {
String json = "{\"list_id\":5,\"contact_id\":42}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/list-contacts");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var content = new StringContent("{\"list_id\":5,\"contact_id\":42}", Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/list-contacts", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "list_id": 5, "contact_id": 42, "added": true }
DELETE /v1/list-contacts
Remove a contact from a list (the contact itself is not deleted).
| Field | Type | Description |
|---|---|---|
list_id | Integer | ID of the list (required). |
contact_id (optional*) | Integer | ID of the contact to remove. One of contact_id or number is required. |
number (optional*) | String | Australian mobile number of the contact to remove. One of contact_id or number is required. |
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/list-contacts \
-H "Content-Type: application/json" \
-d '{"list_id":5,"contact_id":42}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
'https://api.mobilemessage.com.au/v1/list-contacts',
json={"list_id": 5, "contact_id": 42},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/list-contacts', {
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ list_id: 5, contact_id: 42 })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/list-contacts');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["list_id" => 5, "contact_id" => 42]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class RemoveListMember {
public static void main(String[] args) throws Exception {
String json = "{\"list_id\":5,\"contact_id\":42}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/list-contacts");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("DELETE");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.mobilemessage.com.au/v1/list-contacts") {
Content = new StringContent("{\"list_id\":5,\"contact_id\":42}", Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "list_id": 5, "contact_id": 42, "removed": true }
Unsubscribes
Manage your opt-out list. Adding a number blocks it from receiving future messages, cancels any scheduled messages to that number, and removes the contact from all lists.
GET /v1/unsubscribes
| Parameter | Type | Description |
|---|---|---|
number (optional) | String | Filter by phone number. |
limit (optional) | Integer | Results per page. Default 50, max 200. |
offset (optional) | Integer | Pagination offset. Default 0. |
curl -u user123:mypassword "https://api.mobilemessage.com.au/v1/unsubscribes?limit=100&offset=0"
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/unsubscribes',
params={'limit': 100, 'offset': 0},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/unsubscribes?limit=100&offset=0', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/unsubscribes?limit=100&offset=0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetUnsubscribes {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/unsubscribes?limit=100&offset=0");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/unsubscribes?limit=100&offset=0");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"total": 1250,
"limit": 100,
"offset": 0,
"results": [
{ "number": "61412345678", "updated_at": "2026-01-15 09:00:00" }
]
}
POST /v1/unsubscribes
Add a number to your unsubscribe list.
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/unsubscribes \
-H "Content-Type: application/json" \
-d '{"number":"0412345678"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/unsubscribes',
json={"number": "0412345678"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/unsubscribes', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ number: '0412345678' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/unsubscribes');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["number" => "0412345678"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class AddUnsubscribe {
public static void main(String[] args) throws Exception {
String json = "{\"number\":\"0412345678\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/unsubscribes");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var content = new StringContent("{\"number\":\"0412345678\"}", Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/unsubscribes", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "number": "61412345678", "added": true }
DELETE /v1/unsubscribes
Remove a number from your unsubscribe list.
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/unsubscribes \
-H "Content-Type: application/json" \
-d '{"number":"0412345678"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
'https://api.mobilemessage.com.au/v1/unsubscribes',
json={"number": "0412345678"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/unsubscribes', {
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ number: '0412345678' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/unsubscribes');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["number" => "0412345678"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class DeleteUnsubscribe {
public static void main(String[] args) throws Exception {
String json = "{\"number\":\"0412345678\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/unsubscribes");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("DELETE");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.mobilemessage.com.au/v1/unsubscribes") {
Content = new StringContent("{\"number\":\"0412345678\"}", Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "number": "61412345678", "removed": true }
Account Credit Balance
GET /v1/account
Retrieve your current SMS credit balance using your authenticated credentials. No extra parameters are required.
Code Examples
# Execute the GET request and view the response
curl -u user123:mypassword -X GET https://api.mobilemessage.com.au/v1/account
# Example response output:
# {"status": "complete", "credit_balance": 1000}
import requests
from requests.auth import HTTPBasicAuth
url = "https://api.mobilemessage.com.au/v1/account"
response = requests.get(url, auth=HTTPBasicAuth('user123', 'mypassword'))
# Parse the JSON response
data = response.json()
credit_balance = data.get('credit_balance', 'N/A')
print(f"Credit Balance: {credit_balance}")
# Output might be: Credit Balance: 1000
// Using Fetch API to get and process account balance
fetch('https://api.mobilemessage.com.au/v1/account', {
method: 'GET',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
const creditBalance = data.credit_balance || 'N/A';
console.log("Credit Balance:", creditBalance);
})
.catch(error => console.error(error));
<?php
$url = "https://api.mobilemessage.com.au/v1/account";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$creditBalance = isset($data['credit_balance']) ? $data['credit_balance'] : 'N/A';
echo "Credit Balance: " . $creditBalance;
// Output might be: Credit Balance: 1000
?>
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
import org.json.JSONObject; // Requires org.json library
public class GetBalance {
public static void main(String[] args) throws Exception {
URL url = new URL("https://api.mobilemessage.com.au/v1/account");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
connection.setRequestProperty("Authorization", "Basic " + credentials);
connection.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder responseStr = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
responseStr.append(inputLine);
}
in.close();
JSONObject json = new JSONObject(responseStr.toString());
int creditBalance = json.getInt("credit_balance");
System.out.println("Credit Balance: " + creditBalance);
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq; // Requires Newtonsoft.Json package
class Program {
static async Task Main() {
var client = new HttpClient();
var byteArray = Encoding.ASCII.GetBytes("user123:mypassword");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var response = await client.GetAsync("https://api.mobilemessage.com.au/v1/account");
var responseString = await response.Content.ReadAsStringAsync();
var json = JObject.Parse(responseString);
var creditBalance = json["credit_balance"];
Console.WriteLine("Credit Balance: " + creditBalance);
}
}
Example Successful Response (200)
{
"status": "complete",
"credit_balance": 1000
}
Example Error Responses
Account Not Found (404)
{
"error": "Account not found or no credit balance available."
}
Invalid Request Method (405)
{
"error": "Invalid request method. Only GET is allowed."
}
Sender IDs
List your active Sender IDs or register your own mobile number as a sender via a two-step verification flow.
GET /v1/senders
List all active Sender IDs on your account.
curl -u user123:mypassword https://api.mobilemessage.com.au/v1/senders
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/senders',
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/senders', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/senders');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetSenders {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/senders");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/senders");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"results": [
{ "sender": "CompanyABC", "type": "alpha", "label": "My Brand", "is_default": true },
{ "sender": "61412345678", "type": "own", "label": "My mobile", "is_default": false }
]
}
POST /v1/senders
Register your own mobile number as a sender. An SMS containing a confirmation link is sent to the number at no charge. The owner must click the link and confirm before the number is activated as a sender on your account.
| Field | Type | Description |
|---|---|---|
number | String | The mobile number to register. |
label (optional) | String | A label to identify this sender in your account. |
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/senders \
-H "Content-Type: application/json" \
-d '{"number":"0412345678","label":"My mobile"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/senders',
json={"number": "0412345678", "label": "My mobile"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/senders', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ number: '0412345678', label: 'My mobile' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/senders');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["number" => "0412345678", "label" => "My mobile"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class AddSender {
public static void main(String[] args) throws Exception {
String json = "{\"number\":\"0412345678\",\"label\":\"My mobile\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/senders");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var content = new StringContent("{\"number\":\"0412345678\",\"label\":\"My mobile\"}", Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/senders", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "verification_sent", "message": "A verification link has been sent to the number." }
Returns HTTP 409 if the number is already an active sender on your account. Returns HTTP 429 if you have too many pending verifications or a verification was sent to that number in the last 5 minutes.
ACMA Registration
Register a custom alphanumeric Sender ID (e.g. "MyBrand") with the Australian Communications and Media Authority (ACMA). All custom Sender IDs in Australia must be registered through the ACMA Sender ID Register. After submission, registrations are reviewed by Mobile Message before being forwarded to ACMA for approval.
GET /v1/acma-registration
List your ACMA sender ID registrations along with your saved brands and partner organisations. If you include the sender query parameter, the ACMA status of that specific Sender ID is returned instead.
Query parameters
| Parameter | Type | Description |
|---|---|---|
sender (optional) | String | If provided, returns the ACMA registration status for this Sender ID instead of the full listing. |
List brands and partners
curl -u user123:mypassword https://api.mobilemessage.com.au/v1/acma-registration
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/acma-registration',
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/acma-registration', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/acma-registration');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetAcma {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/acma-registration");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/acma-registration");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"registrations": [
{ "sender_id": 3, "sender": "MyBrand", "status": "active", "acma_status": "verified", "brand_id": 1, "partner_id": null },
{ "sender_id": 15, "sender": "NewBrand", "status": "pending", "acma_status": "pending_internal_review", "brand_id": 1, "partner_id": 5 }
],
"brands": [
{ "id": 1, "brand_name": "Example Pty Ltd", "abn": "12345678901", "website": "https://example.com.au" }
],
"partners": [
{ "id": 5, "partner_name": "Partner Agency", "abn": "98765432109", "website": "https://partner.com.au" }
]
}
Response fields
The registrations array contains all custom (alphanumeric) Sender IDs on your account. Each registration has the following fields:
| Field | Description |
|---|---|
sender_id | The internal ID for this sender. Use this as the sender_id field in a POST request if you need to re-register a previously rejected sender. |
sender | The alphanumeric Sender ID text (e.g. "MyBrand"). |
status | The sender's overall status: active (approved and ready to use for sending) or pending (registration in progress, not yet available for sending). |
acma_status |
The current stage of the ACMA registration process:
|
brand_id | The ID of the brand associated with this registration, or null if not yet linked. Corresponds to an entry in the brands array. |
partner_id | The ID of the on-behalf-of partner associated with this registration, or null if not applicable. Corresponds to an entry in the partners array. |
The brands array contains your saved organisations. Pass the id as brand_id with brand_mode set to existing when submitting a registration to reuse a brand without re-entering its details.
The partners array contains your saved on-behalf-of organisations. Pass the id as partner_id when submitting a registration on behalf of a previously used client.
Check sender ACMA status
curl -u user123:mypassword "https://api.mobilemessage.com.au/v1/acma-registration?sender=MyBrand"
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/acma-registration',
params={'sender': 'MyBrand'},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/acma-registration?sender=MyBrand', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/acma-registration?sender=MyBrand');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetAcmaStatus {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/acma-registration?sender=MyBrand");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/acma-registration?sender=MyBrand");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"sender": "MyBrand",
"sender_id": 15,
"acma_status": "pending_internal_review",
"brand_id": 1,
"chatbot_id": 8,
"partner_id": null
}
Returns HTTP 404 if the sender is not found on your account.
POST /v1/acma-registration
Submit a new custom Sender ID for ACMA registration. The registration will be reviewed by Mobile Message before being forwarded to ACMA. You can reuse previously saved brands and partners by passing their id from the GET response.
Sender ID fields
| Field | Type | Description |
|---|---|---|
sender | String | The alphanumeric Sender ID to register. 1–11 characters, printable ASCII only, cannot be only numbers. |
sender_id (optional) | Integer | An existing sender_id from your account to re-register (e.g. after a previous rejection). If omitted, a new sender record is created. |
sender_id_relation_type | String | How the Sender ID relates to the organisation. One of:
|
sender_id_relation_detail | String | Required unless sender_id_relation_type is registered_company_name. The value depends on the relation type:
|
Contact fields (always required)
The contact person at the brand/organisation that the Sender ID belongs to. When registering on behalf of a client, these should be the client's contact details, not yours (your details go in the on-behalf-of fields below).
| Field | Type | Description |
|---|---|---|
contact_first_name | String | Contact first name at the brand/organisation. |
contact_last_name | String | Contact last name at the brand/organisation. |
contact_email | String | Contact email address at the brand/organisation. |
Brand / organisation fields
You can either reuse an existing brand or provide details for a new one.
| Field | Type | Description |
|---|---|---|
brand_mode | String | new (default) or existing. Set to existing to reuse a previously saved brand by its brand_id. |
brand_id | Integer | Required when brand_mode is existing. The brand ID from GET /v1/acma-registration. |
abn | String | Australian Business Number (11 digits, spaces allowed). Required when brand_mode is new. |
business_name | String | Registered business name. Required when brand_mode is new. |
address_line1 | String | Business street address. Required when brand_mode is new. |
address_line2 (optional) | String | Additional address line. |
suburb | String | Suburb or city. Required when brand_mode is new. |
state | String | Australian state or territory: ACT, NSW, NT, QLD, SA, TAS, VIC, or WA. Required when brand_mode is new. |
postcode | String | Postcode. Required when brand_mode is new. |
website | String | Business website URL. Required when brand_mode is new. |
business_phone | String | Business phone number. Required when brand_mode is new. |
On-behalf-of fields (registering on behalf of a client)
Set on_behalf to true when you are registering a Sender ID for a business that is not your own. This is common for marketing agencies, IT service providers, or any business that sends SMS on behalf of their clients.
When registering on behalf of a client, the Brand / organisation fields above should contain the details of the client — the business whose name will appear as the Sender ID. The on-behalf-of fields below should contain your business details as the applicant performing the registration.
For example, if your marketing agency "Acme Marketing" is registering the Sender ID "PizzaCo" for your client "Pizza Company Pty Ltd", the brand fields would contain Pizza Company's ABN, address and contact details, and the on-behalf-of fields would contain Acme Marketing's details.
You can reuse a previously saved partner by passing partner_id from the GET response instead of providing all applicant fields again.
| Field | Type | Description |
|---|---|---|
on_behalf | Boolean | Set to true if registering on behalf of another business. Defaults to false. |
partner_id (optional) | Integer | Reuse an existing partner by ID from GET /v1/acma-registration. If provided, the applicant fields below are not required. |
applicant_abn | String | Applicant's ABN (11 digits). Required when on_behalf is true and no partner_id. |
applicant_name | String | Applicant's organisation name. Required when on_behalf is true and no partner_id. |
applicant_contact_first_name | String | Applicant contact first name. Required when on_behalf is true and no partner_id. |
applicant_contact_last_name | String | Applicant contact last name. Required when on_behalf is true and no partner_id. |
applicant_contact_email | String | Applicant contact email. Required when on_behalf is true and no partner_id. |
applicant_website | String | Applicant's website URL. Required when on_behalf is true and no partner_id. |
applicant_phone (optional) | String | Applicant's business phone number. |
applicant_address_line1 | String | Applicant's street address. Required when on_behalf is true and no partner_id. |
applicant_address_line2 (optional) | String | Additional address line. |
applicant_suburb | String | Applicant's suburb or city. Required when on_behalf is true and no partner_id. |
applicant_state | String | Applicant's state or territory. Required when on_behalf is true and no partner_id. |
applicant_postcode | String | Applicant's postcode. Required when on_behalf is true and no partner_id. |
Example: register with a new brand
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/acma-registration \
-H "Content-Type: application/json" \
-d '{
"sender": "MyBrand",
"sender_id_relation_type": "registered_company_name",
"contact_first_name": "Jane",
"contact_last_name": "Smith",
"contact_email": "jane@example.com.au",
"abn": "12345678901",
"business_name": "Example Pty Ltd",
"address_line1": "123 Collins St",
"suburb": "Melbourne",
"state": "VIC",
"postcode": "3000",
"website": "https://example.com.au",
"business_phone": "0398765432",
}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/acma-registration',
json={
'sender': 'MyBrand',
'sender_id_relation_type': 'registered_company_name',
'contact_first_name': 'Jane',
'contact_last_name': 'Smith',
'contact_email': 'jane@example.com.au',
'abn': '12345678901',
'business_name': 'Example Pty Ltd',
'address_line1': '123 Collins St',
'suburb': 'Melbourne',
'state': 'VIC',
'postcode': '3000',
'website': 'https://example.com.au',
'business_phone': '0398765432',
},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/acma-registration', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
sender: 'MyBrand',
sender_id_relation_type: 'registered_company_name',
contact_first_name: 'Jane',
contact_last_name: 'Smith',
contact_email: 'jane@example.com.au',
abn: '12345678901',
business_name: 'Example Pty Ltd',
address_line1: '123 Collins St',
suburb: 'Melbourne',
state: 'VIC',
postcode: '3000',
website: 'https://example.com.au',
business_phone: '0398765432',
})
})
.then(r => r.json())
.then(console.log);
<?php
$data = [
'sender' => 'MyBrand',
'sender_id_relation_type' => 'registered_company_name',
'contact_first_name' => 'Jane',
'contact_last_name' => 'Smith',
'contact_email' => 'jane@example.com.au',
'abn' => '12345678901',
'business_name' => 'Example Pty Ltd',
'address_line1' => '123 Collins St',
'suburb' => 'Melbourne',
'state' => 'VIC',
'postcode' => '3000',
'website' => 'https://example.com.au',
'business_phone' => '0398765432',
];
$ch = curl_init('https://api.mobilemessage.com.au/v1/acma-registration');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class AcmaRegister {
public static void main(String[] args) throws Exception {
String json = """
{
"sender": "MyBrand",
"sender_id_relation_type": "registered_company_name",
"contact_first_name": "Jane",
"contact_last_name": "Smith",
"contact_email": "jane@example.com.au",
"abn": "12345678901",
"business_name": "Example Pty Ltd",
"address_line1": "123 Collins St",
"suburb": "Melbourne",
"state": "VIC",
"postcode": "3000",
"website": "https://example.com.au",
"business_phone": "0398765432",
}""";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/acma-registration");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = @"{
""sender"": ""MyBrand"",
""sender_id_relation_type"": ""registered_company_name"",
""contact_first_name"": ""Jane"",
""contact_last_name"": ""Smith"",
""contact_email"": ""jane@example.com.au"",
""abn"": ""12345678901"",
""business_name"": ""Example Pty Ltd"",
""address_line1"": ""123 Collins St"",
""suburb"": ""Melbourne"",
""state"": ""VIC"",
""postcode"": ""3000"",
""website"": ""https://example.com.au"",
""business_phone"": ""0398765432"",
}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/acma-registration", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"message": "Sender ID registration submitted and is pending review.",
"sender_id": 15,
"brand_id": 1,
"chatbot_id": 8,
"partner_id": null,
"acma_status": "pending_internal_review"
}
Example: register with an existing brand
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/acma-registration \
-H "Content-Type: application/json" \
-d '{
"sender": "ExBrand",
"brand_mode": "existing",
"brand_id": 1,
"sender_id_relation_type": "registered_domain_name",
"sender_id_relation_detail": "example.com.au",
"contact_first_name": "Jane",
"contact_last_name": "Smith",
"contact_email": "jane@example.com.au",
}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/acma-registration',
json={
'sender': 'ExBrand',
'brand_mode': 'existing',
'brand_id': 1,
'sender_id_relation_type': 'registered_domain_name',
'sender_id_relation_detail': 'example.com.au',
'contact_first_name': 'Jane',
'contact_last_name': 'Smith',
'contact_email': 'jane@example.com.au',
},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/acma-registration', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
sender: 'ExBrand',
brand_mode: 'existing',
brand_id: 1,
sender_id_relation_type: 'registered_domain_name',
sender_id_relation_detail: 'example.com.au',
contact_first_name: 'Jane',
contact_last_name: 'Smith',
contact_email: 'jane@example.com.au',
})
})
.then(r => r.json())
.then(console.log);
<?php
$data = [
'sender' => 'ExBrand',
'brand_mode' => 'existing',
'brand_id' => 1,
'sender_id_relation_type' => 'registered_domain_name',
'sender_id_relation_detail' => 'example.com.au',
'contact_first_name' => 'Jane',
'contact_last_name' => 'Smith',
'contact_email' => 'jane@example.com.au',
];
$ch = curl_init('https://api.mobilemessage.com.au/v1/acma-registration');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class AcmaExistingBrand {
public static void main(String[] args) throws Exception {
String json = """
{
"sender": "ExBrand",
"brand_mode": "existing",
"brand_id": 1,
"sender_id_relation_type": "registered_domain_name",
"sender_id_relation_detail": "example.com.au",
"contact_first_name": "Jane",
"contact_last_name": "Smith",
"contact_email": "jane@example.com.au",
}""";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/acma-registration");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = @"{
""sender"": ""ExBrand"",
""brand_mode"": ""existing"",
""brand_id"": 1,
""sender_id_relation_type"": ""registered_domain_name"",
""sender_id_relation_detail"": ""example.com.au"",
""contact_first_name"": ""Jane"",
""contact_last_name"": ""Smith"",
""contact_email"": ""jane@example.com.au"",
}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/acma-registration", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
Example: register on behalf of another business
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/acma-registration \
-H "Content-Type: application/json" \
-d '{
"sender": "ClientCo",
"brand_mode": "existing",
"brand_id": 1,
"sender_id_relation_type": "registered_business_name",
"sender_id_relation_detail": "Client Company",
"contact_first_name": "Jane",
"contact_last_name": "Smith",
"contact_email": "jane@example.com.au",
"on_behalf": true,
"applicant_abn": "98765432109",
"applicant_name": "Client Company Pty Ltd",
"applicant_contact_first_name": "Bob",
"applicant_contact_last_name": "Jones",
"applicant_contact_email": "bob@clientcompany.com.au",
"applicant_website": "https://clientcompany.com.au",
"applicant_address_line1": "10 George St",
"applicant_suburb": "Sydney",
"applicant_state": "NSW",
"applicant_postcode": "2000",
}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/acma-registration',
json={
'sender': 'ClientCo',
'brand_mode': 'existing',
'brand_id': 1,
'sender_id_relation_type': 'registered_business_name',
'sender_id_relation_detail': 'Client Company',
'contact_first_name': 'Jane',
'contact_last_name': 'Smith',
'contact_email': 'jane@example.com.au',
'on_behalf': True,
'applicant_abn': '98765432109',
'applicant_name': 'Client Company Pty Ltd',
'applicant_contact_first_name': 'Bob',
'applicant_contact_last_name': 'Jones',
'applicant_contact_email': 'bob@clientcompany.com.au',
'applicant_website': 'https://clientcompany.com.au',
'applicant_address_line1': '10 George St',
'applicant_suburb': 'Sydney',
'applicant_state': 'NSW',
'applicant_postcode': '2000',
},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/acma-registration', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
sender: 'ClientCo',
brand_mode: 'existing',
brand_id: 1,
sender_id_relation_type: 'registered_business_name',
sender_id_relation_detail: 'Client Company',
contact_first_name: 'Jane',
contact_last_name: 'Smith',
contact_email: 'jane@example.com.au',
on_behalf: true,
applicant_abn: '98765432109',
applicant_name: 'Client Company Pty Ltd',
applicant_contact_first_name: 'Bob',
applicant_contact_last_name: 'Jones',
applicant_contact_email: 'bob@clientcompany.com.au',
applicant_website: 'https://clientcompany.com.au',
applicant_address_line1: '10 George St',
applicant_suburb: 'Sydney',
applicant_state: 'NSW',
applicant_postcode: '2000',
})
})
.then(r => r.json())
.then(console.log);
<?php
$data = [
'sender' => 'ClientCo',
'brand_mode' => 'existing',
'brand_id' => 1,
'sender_id_relation_type' => 'registered_business_name',
'sender_id_relation_detail' => 'Client Company',
'contact_first_name' => 'Jane',
'contact_last_name' => 'Smith',
'contact_email' => 'jane@example.com.au',
'on_behalf' => true,
'applicant_abn' => '98765432109',
'applicant_name' => 'Client Company Pty Ltd',
'applicant_contact_first_name' => 'Bob',
'applicant_contact_last_name' => 'Jones',
'applicant_contact_email' => 'bob@clientcompany.com.au',
'applicant_website' => 'https://clientcompany.com.au',
'applicant_address_line1' => '10 George St',
'applicant_suburb' => 'Sydney',
'applicant_state' => 'NSW',
'applicant_postcode' => '2000',
];
$ch = curl_init('https://api.mobilemessage.com.au/v1/acma-registration');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
echo curl_exec($ch);
curl_close($ch);
?>
{
"status": "complete",
"message": "Sender ID registration submitted and is pending review.",
"sender_id": 16,
"brand_id": 1,
"chatbot_id": 9,
"partner_id": 5,
"acma_status": "pending_internal_review"
}
GET /v1/acma-registration to list your saved brands and partners and pass their id directly via brand_id or partner_id.
Error responses
| HTTP Status | Meaning |
|---|---|
400 | Validation error — a required field is missing or invalid. The error field describes the issue. |
404 | The specified brand_id, partner_id, or sender_id was not found on your account. |
409 | This Sender ID is already registered or has a pending ACMA submission. Contact support to make changes. |
Inbound SMS
GET /v1/inbound
Retrieve paginated inbound SMS messages and opt-out replies received on your dedicated numbers. For real-time notification of inbound messages, configure a webhook.
| Parameter | Type | Description |
|---|---|---|
from (optional) | String | Filter messages received on or after this date (YYYY-MM-DD, UTC). |
to (optional) | String | Filter messages received before this date (YYYY-MM-DD, UTC). |
limit (optional) | Integer | Results per page. Default 50, max 200. |
offset (optional) | Integer | Pagination offset. Default 0. |
curl -u user123:mypassword "https://api.mobilemessage.com.au/v1/inbound?from=2026-01-01&limit=10"
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/inbound',
params={'from': '2026-01-01', 'limit': 10},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/inbound?from=2026-01-01&limit=10', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/inbound?from=2026-01-01&limit=10');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetInbound {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/inbound?from=2026-01-01&limit=10");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/inbound?from=2026-01-01&limit=10");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{
"status": "complete",
"total": 5,
"limit": 10,
"offset": 0,
"results": [
{
"message_id": "abc123def-456g-7890-hijk-lmnopqrstuvw",
"from": "61412345678",
"to": "61400000000",
"message": "Yes, I'd like more info",
"type": "inbound",
"received_at": "2026-01-15 09:35:00"
}
]
}
Delivery Receipts & Inbound Messages (Webhooks)
Configure webhooks to receive real-time notifications for inbound messages and delivery receipts.
Managing webhook URLs via API
You can get and update your webhook URLs programmatically using the /v1/webhooks endpoint.
GET /v1/webhooks
curl -u user123:mypassword https://api.mobilemessage.com.au/v1/webhooks
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.mobilemessage.com.au/v1/webhooks',
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/webhooks', {
headers: { 'Authorization': 'Basic ' + btoa('user123:mypassword') }
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/webhooks');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
echo curl_exec($ch);
curl_close($ch);
?>
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class GetWebhooks {
public static void main(String[] args) throws Exception {
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/webhooks");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestProperty("Authorization", "Basic " + credentials);
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var resp = await client.GetAsync("https://api.mobilemessage.com.au/v1/webhooks");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "webhooks": { "inbound": "https://yourapp.com/inbound", "status": null }, "has_signing_secret": true }
has_signing_secret is true when a webhook signing secret is set on your account, which means every webhook we send you carries a signature. See Verifying Webhook Signatures.
POST /v1/webhooks — set a webhook URL
| Field | Type | Description |
|---|---|---|
type | String | Webhook type: inbound or status (required). |
url | String | The HTTPS URL to receive webhook notifications (required). |
curl -u user123:mypassword -X POST https://api.mobilemessage.com.au/v1/webhooks \
-H "Content-Type: application/json" \
-d '{"type":"inbound","url":"https://yourapp.com/sms-inbound"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.mobilemessage.com.au/v1/webhooks',
json={"type": "inbound", "url": "https://yourapp.com/sms-inbound"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/webhooks', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ type: 'inbound', url: 'https://yourapp.com/sms-inbound' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/webhooks');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["type" => "inbound", "url" => "https://yourapp.com/sms-inbound"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class SetWebhook {
public static void main(String[] args) throws Exception {
String json = "{\"type\":\"inbound\",\"url\":\"https://yourapp.com/sms-inbound\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/webhooks");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var json = "{\"type\":\"inbound\",\"url\":\"https://yourapp.com/sms-inbound\"}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.mobilemessage.com.au/v1/webhooks", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "message": "Webhook subscribed successfully", "type": "inbound", "url": "https://yourapp.com/sms-inbound" }
The URL must be 1024 characters or fewer and resolve to a public host — URLs pointing at private or internal addresses are rejected with HTTP 400.
DELETE /v1/webhooks — remove a webhook URL
| Field | Type | Description |
|---|---|---|
type | String | Webhook type to remove: inbound or status (required). |
curl -u user123:mypassword -X DELETE https://api.mobilemessage.com.au/v1/webhooks \
-H "Content-Type: application/json" \
-d '{"type":"inbound"}'
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
'https://api.mobilemessage.com.au/v1/webhooks',
json={"type": "inbound"},
auth=HTTPBasicAuth('user123', 'mypassword')
)
print(response.json())
fetch('https://api.mobilemessage.com.au/v1/webhooks', {
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + btoa('user123:mypassword'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ type: 'inbound' })
})
.then(r => r.json())
.then(console.log);
<?php
$ch = curl_init('https://api.mobilemessage.com.au/v1/webhooks');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "user123:mypassword");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["type" => "inbound"]));
echo curl_exec($ch);
curl_close($ch);
?>
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class DeleteWebhook {
public static void main(String[] args) throws Exception {
String json = "{\"type\":\"inbound\"}";
String credentials = Base64.getEncoder().encodeToString("user123:mypassword".getBytes());
URL url = new URL("https://api.mobilemessage.com.au/v1/webhooks");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("DELETE");
c.setRequestProperty("Authorization", "Basic " + credentials);
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
try (OutputStream os = c.getOutputStream()) { os.write(json.getBytes("utf-8")); }
// Handle response...
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program {
static async Task Main() {
var client = new HttpClient();
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user123:mypassword"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.mobilemessage.com.au/v1/webhooks") {
Content = new StringContent("{\"type\":\"inbound\"}", Encoding.UTF8, "application/json")
};
var resp = await client.SendAsync(request);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
}
}
{ "status": "complete", "message": "Webhook unsubscribed successfully", "type": "inbound" }
Webhook URL Setup
Set your Inbound and/or Status URL in your account settings (or via the API above). These URLs will receive POST requests with a JSON payload when an event occurs.
Webhook Payload Structure
| Field | Type | Description |
|---|---|---|
to |
String | Recipient's phone number. |
message |
String | Content of the SMS. |
sender |
String | The sender's ID or phone number. |
received_at |
String | UTC timestamp when the event was received. |
type (inbound only) |
String | Either "inbound" or "unsubscribe". |
original_message_id (inbound only) |
String | The UUID of the original outbound message. Empty if no matching outbound message was found. |
original_custom_ref (inbound only) |
String | Your custom reference for the original message. Empty if none was provided. |
status (status webhooks only) |
String | Either "delivered" or "failed". |
message_id (status webhooks only) |
String | The unique message ID. |
custom_ref (status webhooks only) |
String | Your custom reference (if provided) for the outbound message. |
part_number (status webhooks only) |
Number |
The part number for this delivery receipt.
For non-concatenated messages this will be 1.
|
total_parts (status webhooks only) |
Number |
Total number of parts for the outbound message.
For non-concatenated messages this will be 1.
|
Delivery, Timeouts and Retries
Webhooks are delivered as HTTP POST requests with a 5 second connection timeout and a 5 second response timeout. Any 2xx response marks the webhook as delivered. Any other response, or a timeout, schedules a retry.
- Up to 10 attempts are made per event.
- Retries back off exponentially: 1 minute after the first failure, then 2, 4, 8, 16 and 32 minutes, capping at 60 minutes between later attempts (each delay includes a small random jitter). An endpoint that stays unreachable is retried for roughly 4 hours in total before the event is marked failed.
- Each attempt is a fresh request. If you use webhook signing, the
X-MM-TimestampandX-MM-Signatureheaders are regenerated for every attempt, so retried deliveries always pass timestamp-freshness checks. - Webhooks are not guaranteed to arrive in order. Use the payload fields (
received_at, andpart_number/total_partson status webhooks) rather than arrival order. - Respond quickly and process asynchronously: a response slower than 5 seconds counts as a failed attempt and will be retried, which can deliver the same event more than once. Make your handler idempotent, for example on
message_idpluspart_numberfor status webhooks.
Verifying Webhook Signatures (Optional)
You can generate a webhook signing secret in your account under Settings then API, in the Webhook Signing Secret section. Once a secret exists, every inbound and status webhook we POST to you carries two extra headers that let you confirm the request really came from Mobile Message before you act on it.
Signing is optional. If you have no signing secret, your webhooks are delivered exactly as before with no extra headers and nothing you already have will break.
| Header | Description |
|---|---|
X-MM-Timestamp |
The unix timestamp in seconds at the moment the request was signed. |
X-MM-Signature |
Lowercase hex HMAC-SHA256 of the signing string, keyed with your signing secret. |
The signing string is the timestamp, a full stop, then the raw request body.
{timestamp}.{raw_body}
{timestamp} is the value of the X-MM-Timestamp header and
{raw_body} is the exact body bytes we sent, read before any JSON parsing. Parsing and
re-encoding the JSON can change whitespace or key order and will produce a different signature, so
always capture the raw body first. Recompute the HMAC with your secret and compare it to
X-MM-Signature using a timing-safe comparison such as hash_equals. Reject
the request if it does not match.
We also recommend rejecting any request where X-MM-Timestamp is more than 5 minutes
away from your own clock, so an old request captured by someone else cannot be replayed at you
later. Signatures are recomputed fresh on every retry attempt, so a retried webhook always arrives
with a current timestamp and a matching signature.
abc123, the timestamp
1754640000 and the raw body {"test":1}, the signing string is
1754640000.{"test":1} and the signature is
52344b9592722e0241d82036e0920f4286bc0d47ba4624c5a1588193490a1efb.
Run your verification code against these values to confirm it produces the same result.
<?php
$secret = 'your_signing_secret';
$timestamp = $_SERVER['HTTP_X_MM_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_MM_SIGNATURE'] ?? '';
$rawBody = file_get_contents('php://input');
// Reject anything signed more than 5 minutes away from now
if ($timestamp === '' || abs(time() - (int) $timestamp) > 300) {
http_response_code(400);
exit('Stale timestamp');
}
$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
// Only parse the body once the signature checks out
$data = json_decode($rawBody, true);
http_response_code(200);
?>
import hashlib
import hmac
import time
from flask import Flask, abort, request
SECRET = 'your_signing_secret'
app = Flask(__name__)
@app.route('/webhook-inbound', methods=['POST'])
def inbound_webhook():
timestamp = request.headers.get('X-MM-Timestamp', '')
signature = request.headers.get('X-MM-Signature', '')
raw_body = request.get_data(as_text=True)
# Reject anything signed more than 5 minutes away from now
if not timestamp or abs(time.time() - int(timestamp)) > 300:
abort(400)
expected = hmac.new(
SECRET.encode(),
f"{timestamp}.{raw_body}".encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
abort(401)
# Only parse the body once the signature checks out
data = request.get_json(force=True)
return '', 200
const express = require('express');
const crypto = require('crypto');
const SECRET = 'your_signing_secret';
const app = express();
// Keep the raw body, the signature is calculated over the exact bytes we sent
app.use(express.json({
verify: (req, res, buf) => { req.rawBody = buf.toString('utf8'); }
}));
app.post('/webhook-inbound', (req, res) => {
const timestamp = req.headers['x-mm-timestamp'] || '';
const signature = req.headers['x-mm-signature'] || '';
// Reject anything signed more than 5 minutes away from now
if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return res.sendStatus(400);
}
const expected = crypto.createHmac('sha256', SECRET)
.update(`${timestamp}.${req.rawBody}`)
.digest('hex');
const a = Buffer.from(expected, 'utf8');
const b = Buffer.from(signature, 'utf8');
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.sendStatus(401);
}
// req.body holds the parsed webhook payload
console.log(req.body);
res.sendStatus(200);
});
app.listen(3000);
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class WebhookSignature {
private static final String SECRET = "your_signing_secret";
public static boolean isValid(String timestamp, String signature, String rawBody) throws Exception {
if (timestamp == null || signature == null) {
return false;
}
// Reject anything signed more than 5 minutes away from now
long skew = Math.abs(System.currentTimeMillis() / 1000L - Long.parseLong(timestamp));
if (skew > 300) {
return false;
}
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal((timestamp + "." + rawBody).getBytes(StandardCharsets.UTF_8));
StringBuilder expected = new StringBuilder();
for (byte b : hash) {
expected.append(String.format("%02x", b));
}
// Constant-time comparison
return MessageDigest.isEqual(
expected.toString().getBytes(StandardCharsets.UTF_8),
signature.getBytes(StandardCharsets.UTF_8)
);
}
}
using System;
using System.Security.Cryptography;
using System.Text;
class WebhookSignature {
const string Secret = "your_signing_secret";
public static bool IsValid(string timestamp, string signature, string rawBody) {
if (string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(signature)) {
return false;
}
// Reject anything signed more than 5 minutes away from now
var skew = Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(timestamp));
if (skew > 300) {
return false;
}
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(Secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{timestamp}.{rawBody}"));
var expected = Convert.ToHexString(hash).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected),
Encoding.UTF8.GetBytes(signature)
);
}
}
Matching a Reply to the Message It Answers
original_message_id is the same value returned as results[].message_id
when you sent the message.
Every recipient gets its own message_id. A send request with ten
recipients returns ten results, each with a different message_id, even when the message
text is identical and they all belong to the same job, order or campaign in your system. Store each
message_id against the recipient it was returned for. Keeping a single ID per job means
later recipients overwrite earlier ones and their replies stop matching.
A reply is matched on the two phone numbers involved. We link it to the most recent message sent to that mobile number from the number they replied to. If you send the same person several messages from the same sender, their reply matches the latest one.
To match on your own identifier instead, set custom_ref when you send.
It is returned on the reply as original_custom_ref, and unlike message_id
you control the value, so it can carry your own job or order number across every recipient of that
job. Those messages can also be retrieved later with
GET /v1/messages?custom_ref=YOUR_REF, which returns each recipient along with its
message_id.
Example Inbound Webhook Payload
{
"to": "61412345678",
"message": "Hello, this is message 1",
"sender": "61412345699",
"received_at": "2024-09-30 14:35:00",
"type": "inbound",
"original_message_id": "db6190e1-1ce8-4cdd-b871-244257d57abc",
"original_custom_ref": "tracking001"
}
Example Status Webhook Payload
{
"to": "61412345678",
"message": "Hello, this is message 1",
"sender": "Mobile MSG",
"custom_ref": "tracking001",
"status": "delivered",
"message_id": "044b035f-0396-4a47-8428-12d5273ab04a",
"received_at": "2024-09-30 14:35:00",
"part_number": 1,
"total_parts": 1
}
Example Status Webhook Payload (Concatenated Message)
{
"to": "61412345678",
"message": "Hello, this is a longer message that was split into multiple parts",
"sender": "Mobile MSG",
"custom_ref": "tracking001",
"status": "delivered",
"message_id": "044b035f-0396-4a47-8428-12d5273ab04a",
"received_at": "2024-09-30 14:35:00",
"part_number": 2,
"total_parts": 3
}
Inbound Webhook Examples
Below are sample implementations for receiving inbound SMS messages. If you have a webhook signing secret, add the check from Verifying Webhook Signatures before you read the payload in any of these examples.
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhook-inbound', methods=['POST'])
def inbound_webhook():
data = request.get_json(force=True)
# Extract inbound fields
to = data.get('to', '')
message = data.get('message', '')
sender = data.get('sender', '')
received_at = data.get('received_at', '')
inbound_type = data.get('type', '')
original_message_id = data.get('original_message_id', '')
original_custom_ref = data.get('original_custom_ref', '')
print("Inbound Webhook Received:")
print(f" To: {to}")
print(f" From: {sender}")
print(f" Received At: {received_at}")
print(f" Message: {message}")
print(f" Type: {inbound_type}")
print(f" Original Message ID: {original_message_id}")
print(f" Original Custom Ref: {original_custom_ref}")
# Respond with simple 'OK'
return "OK", 200
if __name__ == '__main__':
app.run(port=5000)
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook-inbound', (req, res) => {
const {
to,
message,
sender,
received_at,
type,
original_message_id,
original_custom_ref
} = req.body;
console.log("Inbound Webhook Received:");
console.log(" To:", to);
console.log(" From:", sender);
console.log(" Received At:", received_at);
console.log(" Message:", message);
console.log(" Type:", type);
console.log(" Original Message ID:", original_message_id);
console.log(" Original Custom Ref:", original_custom_ref);
res.status(200).send("OK");
});
app.listen(3000, () => {
console.log('Inbound webhook server listening on port 3000');
});
<?php
// Simple inbound webhook example in PHP
$rawData = file_get_contents('php://input');
$data = json_decode($rawData, true);
$to = $data['to'] ?? '';
$message = $data['message'] ?? '';
$sender = $data['sender'] ?? '';
$receivedAt = $data['received_at'] ?? '';
$type = $data['type'] ?? '';
$originalMessageId = $data['original_message_id'] ?? '';
$originalCustomRef = $data['original_custom_ref'] ?? '';
error_log("Inbound Webhook Received:");
error_log(" To: $to");
error_log(" From: $sender");
error_log(" Received At: $receivedAt");
error_log(" Message: $message");
error_log(" Type: $type");
error_log(" Original Message ID: $originalMessageId");
error_log(" Original Custom Ref: $originalCustomRef");
// Send a simple OK response
http_response_code(200);
echo "OK";
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
@RestController
public class InboundWebhookController {
@PostMapping("/webhook-inbound")
public ResponseEntity<String> handleInbound(@RequestBody InboundPayload payload) {
System.out.println("Inbound Webhook Received:");
System.out.println(" To: " + payload.getTo());
System.out.println(" From: " + payload.getSender());
System.out.println(" Received At: " + payload.getReceivedAt());
System.out.println(" Message: " + payload.getMessage());
System.out.println(" Type: " + payload.getType());
System.out.println(" Original Message ID: " + payload.getOriginalMessageId());
System.out.println(" Original Custom Ref: " + payload.getOriginalCustomRef());
return ResponseEntity.ok("OK");
}
}
// Example inbound payload model
class InboundPayload {
private String to;
private String message;
private String sender;
private String receivedAt;
private String type;
private String originalMessageId;
private String originalCustomRef;
// Getters and setters...
}
using Microsoft.AspNetCore.Mvc;
using System;
[ApiController]
[Route("webhook-inbound")]
public class InboundWebhookController : ControllerBase {
[HttpPost]
public IActionResult HandleInbound([FromBody] InboundPayload payload) {
Console.WriteLine("Inbound Webhook Received:");
Console.WriteLine($" To: {payload.To}");
Console.WriteLine($" From: {payload.Sender}");
Console.WriteLine($" Received At: {payload.ReceivedAt}");
Console.WriteLine($" Message: {payload.Message}");
Console.WriteLine($" Type: {payload.Type}");
Console.WriteLine($" Original Message ID: {payload.OriginalMessageId}");
Console.WriteLine($" Original Custom Ref: {payload.OriginalCustomRef}");
return Ok("OK");
}
}
public class InboundPayload {
public string To { get; set; }
public string Message { get; set; }
public string Sender { get; set; }
public string ReceivedAt { get; set; }
public string Type { get; set; }
public string OriginalMessageId { get; set; }
public string OriginalCustomRef { get; set; }
}
Status Webhook Examples
Below are sample implementations for receiving message delivery status updates. These use fields status and message_id, plus any custom reference used when sending the message.
Each status webhook also includes part_number and total_parts so you can track delivery for concatenated messages.
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhook-status', methods=['POST'])
def status_webhook():
data = request.get_json(force=True)
# Extract status-related fields
to = data.get('to', '')
message = data.get('message', '')
sender = data.get('sender', '')
custom_ref = data.get('custom_ref', '')
status = data.get('status', '')
message_id = data.get('message_id', '')
received_at = data.get('received_at', '')
part_number = data.get('part_number', 1)
total_parts = data.get('total_parts', 1)
print("Status Webhook Received:")
print(f" To: {to}")
print(f" From: {sender}")
print(f" Received At: {received_at}")
print(f" Message: {message}")
print(f" Custom Ref: {custom_ref}")
print(f" Status: {status}")
print(f" Message ID: {message_id}")
print(f" Part: {part_number}/{total_parts}")
return "OK", 200
if __name__ == '__main__':
app.run(port=5000)
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook-status', (req, res) => {
const {
to,
message,
sender,
custom_ref,
status,
message_id,
received_at,
part_number,
total_parts
} = req.body;
console.log("Status Webhook Received:");
console.log(" To:", to);
console.log(" From:", sender);
console.log(" Received At:", received_at);
console.log(" Message:", message);
console.log(" Custom Ref:", custom_ref);
console.log(" Status:", status);
console.log(" Message ID:", message_id);
console.log(" Part:", `${part_number}/${total_parts}`);
res.status(200).send("OK");
});
app.listen(3000, () => {
console.log('Status webhook server listening on port 3000');
});
<?php
$rawData = file_get_contents('php://input');
$data = json_decode($rawData, true);
$to = $data['to'] ?? '';
$message = $data['message'] ?? '';
$sender = $data['sender'] ?? '';
$customRef = $data['custom_ref'] ?? '';
$status = $data['status'] ?? '';
$messageId = $data['message_id'] ?? '';
$receivedAt = $data['received_at'] ?? '';
$partNumber = $data['part_number'] ?? 1;
$totalParts = $data['total_parts'] ?? 1;
error_log("Status Webhook Received:");
error_log(" To: $to");
error_log(" From: $sender");
error_log(" Received At: $receivedAt");
error_log(" Message: $message");
error_log(" Custom Ref: $customRef");
error_log(" Status: $status");
error_log(" Message ID: $messageId");
error_log(" Part: {$partNumber}/{$totalParts}");
http_response_code(200);
echo "OK";
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
@RestController
public class StatusWebhookController {
@PostMapping("/webhook-status")
public ResponseEntity<String> handleStatus(@RequestBody StatusPayload payload) {
System.out.println("Status Webhook Received:");
System.out.println(" To: " + payload.getTo());
System.out.println(" From: " + payload.getSender());
System.out.println(" Received At: " + payload.getReceivedAt());
System.out.println(" Message: " + payload.getMessage());
System.out.println(" Custom Ref: " + payload.getCustomRef());
System.out.println(" Status: " + payload.getStatus());
System.out.println(" Message ID: " + payload.getMessageId());
System.out.println(" Part: " + payload.getPartNumber() + "/" + payload.getTotalParts());
return ResponseEntity.ok("OK");
}
}
// Example status payload model
class StatusPayload {
private String to;
private String message;
private String sender;
private String customRef;
private String status;
private String messageId;
private String receivedAt;
private Integer partNumber;
private Integer totalParts;
// Getters and setters...
}
using Microsoft.AspNetCore.Mvc;
using System;
[ApiController]
[Route("webhook-status")]
public class StatusWebhookController : ControllerBase {
[HttpPost]
public IActionResult HandleStatus([FromBody] StatusPayload payload) {
Console.WriteLine("Status Webhook Received:");
Console.WriteLine($" To: {payload.To}");
Console.WriteLine($" From: {payload.Sender}");
Console.WriteLine($" Received At: {payload.ReceivedAt}");
Console.WriteLine($" Message: {payload.Message}");
Console.WriteLine($" Custom Ref: {payload.CustomRef}");
Console.WriteLine($" Status: {payload.Status}");
Console.WriteLine($" Message ID: {payload.MessageId}");
Console.WriteLine($" Part: {payload.PartNumber}/{payload.TotalParts}");
return Ok("OK");
}
}
public class StatusPayload {
public string To { get; set; }
public string Message { get; set; }
public string Sender { get; set; }
public string CustomRef { get; set; }
public string Status { get; set; }
public string MessageId { get; set; }
public string ReceivedAt { get; set; }
public int PartNumber { get; set; }
public int TotalParts { get; set; }
}
Testing Your Integration
Test (sandbox) accounts are available on request. Email hello@mobilemessage.com.au and we'll set one up for you.
A test account behaves exactly like a live account at the API level, with sending disconnected:
- Requests are validated and answered exactly as on a live account: you receive real
message_idvalues, per-message results, and cost calculations in responses. - No messages are ever delivered to handsets, and no credits are consumed.
- Accepted messages are recorded with a final status, so message lookup flows can be exercised end to end. Scheduled messages are released at their scheduled time and given their final status the same way.
- Delivery receipt webhooks fire for test sends. Around 5 seconds after a message is accepted, a simulated delivery receipt is posted to your status webhook URL with the same payload as a real one, one webhook per message part. If your account has a signing secret, simulated webhooks are signed exactly like real ones.
- To test your failure handling, send to any number ending in
000(for example0412345000). That message is recorded asfailedand its delivery receipt reportsfailed. All other test messages are reported asdelivered. - Inbound messages and inbound webhooks can be simulated with the test inbound endpoint below.
- Contacts, lists, unsubscribes, idempotency keys and the other account endpoints behave exactly as live.
This makes a test account safe for CI: your integration tests can run against the real API with no risk of live SMS traffic or charges.
Simulating Inbound Messages
Test accounts can simulate a reply with POST /v1/test-inbound. This records the inbound message on your account exactly as a real reply would, including opt-out processing when the message starts with STOP, and posts to your inbound webhook URL if one is set. The endpoint returns 403 on live accounts and allows up to 100 simulated inbound messages per hour.
| Field | Type | Description |
|---|---|---|
from | String | The mobile number the reply comes from (required). |
message | String | The reply content (required). Start it with STOP to simulate an opt-out. |
to | String | Which of your sender IDs received the reply (optional). Defaults to the sender of the last message you sent to that number. |
curl -u user123:mypassword \
-H "Content-Type: application/json" \
-d '{"from": "0412345678", "message": "Thanks, see you then!"}' \
https://api.mobilemessage.com.au/v1/test-inbound
{ "status": "success", "type": "inbound", "to": "YourBrand", "from": "61412345678", "webhook_queued": true }
The inbound webhook payload is identical to a real inbound message, including original_message_id and original_custom_ref when the reply matches a message you previously sent to that number.
Common API Errors and Solutions
| Error Message | HTTP/Message Code | Description and Solution |
|---|---|---|
Unauthorized |
401 | Invalid API username or password. Check your credentials and ensure they are encoded correctly in your request. |
Missing or invalid "messages" parameter. |
400 | The request body must contain a valid "messages" array. Ensure the JSON structure is correct and the "messages" array is present. |
Request body is not valid JSON. |
400 | The request body could not be parsed as JSON. This is often a shell-quoting issue — for example, the Windows Command Prompt strips the double quotes from the JSON. Send a valid JSON object. |
Request body must be a JSON object. |
400 | The body was valid JSON but not an object (for example a JSON array or a bare value). Wrap your fields in a single JSON object: { ... }. |
"<field>" is required. |
400 | A required field is missing or empty. The error message names the exact field — for example "list_id", "sender", or "message". |
Invalid phone number format |
error | The phone number provided is incorrectly formatted. Ensure numbers are in either Australian local or international format. |
Message content cannot be empty |
error | Your message content is empty. |
Invalid sender. You do not have permission to use this sender. |
error | You have attempted to use a sender ID not registered in your account. Register the sender ID first or select an authorised sender. To view your sender IDs, login to your account and click Settings > Sender IDs. |
The recipient has unsubscribed and cannot receive messages. |
blocked | The recipient has unsubscribed from your messages. Remove this number from your recipient list or contact the recipient directly. |
Message contains non-GSM characters |
error | Your message contains unsupported characters (e.g., emojis). Use standard GSM characters only. |
Message exceeds the maximum allowed length. |
error | Reduce your message length. Limits are: • GSM-7: 1530 characters (10 parts) • UCS-2: 670 characters (10 parts) |
Insufficient credits to send the batch of messages. |
403 | You do not have enough SMS credits for the request. Please add more credits to your account. |
Too many concurrent requests. Please wait. |
429 | Your account has reached the maximum number of simultaneous requests (5). Wait briefly before trying again. |
Invalid request method. |
405 | The HTTP method used is not supported by this endpoint. Check the documentation for the correct method (GET, POST, or DELETE). |
Message not found or is not in a scheduled state. |
404 / 409 | Returned when attempting to cancel a message that does not exist (404) or, for message_id cancels, is not currently scheduled (409). send_id and custom_ref cancels return 404 when no scheduled messages remain. |
This phone number already exists in your contacts. |
409 | Duplicate contact blocked. Your account has duplicate contacts disabled. Use a different number or enable duplicates in your account settings. |
You have too many pending verifications. |
429 | You have reached the limit of 3 pending sender verifications. Wait for existing ones to complete or expire before adding another. |
Simple API
GET /simple/send-sms.php
When you can’t send a JSON body, use our Simple API endpoint. All parameters go in the query string.
Example request URLs
GSM-7 (default, non-GSM characters are stripped):
https://api.mobilemessage.com.au/simple/send-sms.php?api_username=YOUR_USERNAME&api_password=YOUR_PASSWORD&sender=61412345678&to=61498765432&message=Hello+there&custom_ref=OptionalRef123
Allow Unicode when required (uses UCS-2 and 70/67 per-part limits):
https://api.mobilemessage.com.au/simple/send-sms.php?api_username=YOUR_USERNAME&api_password=YOUR_PASSWORD&sender=61412345678&to=61498765432&message=Hello+🌏&unicode=true
Query parameters
| Parameter | Required? | Description |
|---|---|---|
api_username |
Yes | Your API username. |
api_password |
Yes | Your API password. |
sender |
Yes | Your approved sender ID or phone number (for example 61412345678). Use GET /v1/senders to list your available senders. |
to |
Yes | Recipient’s phone number in international format (for example 61498765432). |
message |
Yes | URL-encoded SMS text (spaces as +, special characters percent-encoded). |
custom_ref |
No | Your own tracking reference. |
unicode |
No | Set to true to allow UCS-2 when needed. If not set, non-GSM characters are stripped and GSM-7 limits apply. |
max_parts |
No | Maximum SMS parts (credits). Returns 400 if the message exceeds this limit. Default 10, range 1–99. |
ignore_unsubscribes |
No |
Set to true to bypass unsubscribe blocking for this send.
If omitted or set to false, normal unsubscribe blocking applies.
Use with caution, as bypassing your unsubscribe list could result in spam complaints.
|
Length limits
By default up to 10 parts per message (GSM-7: 1530 septets; UCS-2: 670 characters). Use max_parts to lower or raise this limit (1–99).
Response
On success, the Simple API returns a single response object.
{
"status": "success",
"message_id": "5f8b1c22-3f7a-4b5e-9a08-2e62d4f7c9b1",
"custom_ref": "dev-test-1708869123",
"to": "61412345678",
"sender": "61400000000",
"message": "API dev test message 2026-02-25 17:10:00",
"cost": 1,
"ignore_unsubscribes": true
}