Introduction
The purpose of this document is to describe LINK Mobility Messaging API.
The API can be used to send SMS and Viber messages.
The API uses REST technology with POST methods to HTTPS Endpoints.
LINK Mobility provides 3rd parties with api_key, api_secret, service_id and list of allowed senders.
Base URL's
Type | URL |
---|---|
Production | https://api.msghub.cloud/ |
Testing | https://api-test.msghub.cloud/ |
API methods
Method | Description | Example |
---|---|---|
/send | Send message to end-user | https://api.msghub.cloud/send |
/dlr | Check status of previously send message | https://api.msghub.cloud/dlr |
Send Request Body
SMS Message
Example JSON (SMS)
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"registered_delivery": true,
"callback_url": "http:\/\/example.com\/index.php",
"priority": "1024",
"delay": "+11 hours",
"context": {
"foo": "bar"
},
"max_sms_size": 1,
"unique": true,
"parent_msg_id": "goGkSsMG2bpMtZDBoX8v"
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
text | Message to send | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
registered_delivery | Request DLR for the message | boolean | No |
callback_url | User defined url to receive dlr | integer | No |
priority | Priority of the message. Default: 1024 | integer/string | No |
delay | Message delay in seconds or string | integer/string | No |
context | User defined context | array | No |
max_sms_size | Maximum sms’s to send | integer | No |
unique | Do not duplicate message | boolean | No |
parent_msg_id | 3rd party message ID | string | No |
Viber text message
Example JSON (Viber text)
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"ttl": 120,
"platform": 1,
"registered_delivery": true,
"callback_url": "http:\/\/example.com\/index.php",
"priority": "1024",
"delay": "+11 hours",
"context": {
"foo": "bar"
},
"max_sms_size": 1,
"unique": true,
"parent_msg_id": "goGkSsMG2bpMtZDBoX8v"
"fallback": {
"sms": "Test Message SMS"
}
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
text | Message to send | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
registered_delivery | Request DLR for the message | boolean | No |
callback_url | User defined url to receive dlr | integer | No |
priority | Priority of the message. Default: 1024 | integer/string | No |
delay | Message delay in seconds or string | integer/string | No |
context | User defined context | array | No |
max_sms_size | Maximum sms’s to send | integer | No |
unique | Do not duplicate message | boolean | No |
parent_msg_id | 3rd party message ID | string | No |
fallback | Array of fallback texts | array | No |
ttl | Time to live for the message in seconds | integer | No |
platform | End user platform. Default: 2 | integer | No |
Viber message with button
Example JSON (Viber with button)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"text": "Test Viber Message",
"service_id": "1",
"ButtonName": "Cool Button",
"ButtonUrl": "https:\/\/www.linkmobility.bg\/bg"
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
text | Message to send | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
ButtonName | Viber Button Text. Maximum allowed size is 30 characters. | string | Yes |
ButtonUrl | The URL to redirect to | string | Yes |
Viber message with image
Example JSON (Viber with image)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"service_id": "1",
"ImageUrl": "https:\/\/example.com\/example.jpg"
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
ImageUrl | Image URL. Expected formats - PNG,JPEG,jpg. Recommended size: 215x185 | string | Yes |
Viber message with text,image & button
Example JSON (Viber with text,image & button)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"text": "Test Viber Message",
"service_id": "1",
"ImageUrl": "https:\/\/example.com\/example.jpg",
"ButtonName": "Cool Button",
"ButtonUrl": "https:\/\/www.linkmobility.bg\/bg"
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
text | Message to send | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
ButtonName | Viber Button Text. Maximum allowed size is 30 characters. | string | Yes |
ButtonUrl | The URL to redirect to | string | Yes |
ImageUrl | Image URL. Expected formats - PNG,JPEG,jpg. Recommended size: 215x185 | string | Yes |
Viber message with video
Example JSON (Viber with video)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"service_id": "1",
"ThumbnailUrl": "https:\/\/example.com\/example.jpg",
"VideoUrl": "https://storage.googleapis.com/viber-images/1016444621613da75f04.mp4",
"VideoSize": 10,
"Duration": 30
}
Example JSON (Viber with text & video)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"service_id": "1",
"text": "Test Viber Message",
"ThumbnailUrl": "https:\/\/example.com\/example.jpg",
"VideoUrl": "https://storage.googleapis.com/viber-images/1016444621613da75f04.mp4",
"VideoSize": 10,
"Duration": 30
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
text | Message to send | string | No |
VideoUrl | Video Message URL. Supported formats: 3gp, m4v, mov, mp4 | string | Yes |
ThumbnailUrl | Viber Video Thumbnail URL | string | Yes |
VideoSize | Video Size in MB. Maximum size: 200mb | integer | Yes |
Duration | Video Duration in seconds | integer | Yes |
Viber message with text, video & button
Example JSON (Viber with text, video & button)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"service_id": "1",
"text": "Test Viber Message",
"ButtonName": "Cool Button",
"ThumbnailUrl": "https:\/\/example.com\/example.jpg",
"VideoUrl": "https://storage.googleapis.com/viber-images/1016444621613da75f04.mp4",
"VideoSize": 10,
"Duration": 30
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
text | Message to send | string | Yes |
ButtonName | Viber Button Text. Maximum allowed size is 30 characters. | string | Yes |
VideoUrl | Video Message URL. Supported formats: 3gp, m4v, mov, mp4 | string | Yes |
ThumbnailUrl | Viber Video Thumbnail URL | string | Yes |
VideoSize | Video Size in MB. Maximum size: 200mb | integer | Yes |
Duration | Video Duration in seconds | integer | Yes |
Viber message with file
Example JSON (Viber with file)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"service_id": "1",
"FileUrl": "https://cdn.storage/files/example.pdf",
"FileName": "Example Filename.pdf"
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
FileUrl | File URL. Maximum size: 200mb. Supported formats: doc, docx, rtf, dot, dotx, odt, odf, fodt, txt, info, pdf, xps, pdax, eps, xls, xlsx, ods, fods, csv, xlsm, xltx | string | Yes |
FileName | Custom filename | string | No |
Viber message with survey(list)
Example JSON (Viber with survey)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"text": "Survey title text",
"service_id": "1",
"survey": [
"option1 text",
"option2 text",
"option3 text",
"option4 text",
"option5 text"
]
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
text | Text for Survey Title (Up to 85 characters) | string | Yes |
survey | Array of survey options (Number of options can be from 2 to 5, Up to 50 characters per option) | array | Yes |
Viber message with carousel
Example JSON (Viber with carousel)
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"text": "Carousel title text",
"service_id": "1",
"carousel": [
{
"title": "Carousel item1 title",
"imageUrl": "https:\/\/example.com\/example.jpg",
"primaryButton": {
"label": "primary button name",
"actionUrl": "https:\/\/www.linkmobility.bg\/"
},
"secondaryButton": {
"label": "secondary button name",
"actionUrl": "https:\/\/www.linkmobility.bg\/bg"
}
},
{
"title": "Carousel item2 title",
"imageUrl": "https:\/\/example.com\/example2.jpg",
"primaryButton": {
"label": "primary button name",
"actionUrl": "https:\/\/www.linkmobility.bg\/"
}
}
]
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
msisdn | Valid end-user mobile number | string | Yes |
sc | Sender / Display name | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
text | Text for Carousel Title Up to 1000 characters. Recommended length: 200 characters. | string | Yes |
carousel | Array of carousel items (Number of items can be from 2 to 5) | array | Yes |
CarouselItem | Description | Type | Mandatory |
---|---|---|---|
title | Item title text. From 2 to 38 characters. | string | Yes |
imageUrl | Item image URL. Expected formats - PNG,JPEG,jpg. Recommended size: 215x185 | string | Yes |
primaryButton | Item Primary button object. Contains Mandatory params label (Up to 10 characters) and actionUrl | object | Yes |
secondaryButton | Item Secondary button object. Contains Mandatory params label (Up to 12 characters) and actionUrl | object | No |
Push Notification
Example JSON (Push Notification)
{
"sc": "PushTest",
"service_id": "1",
"fallback": {
"sms": "Test Message SMS"
},
"push": {
"title": "Example title!",
"body": "Example body description!",
"uid": "cloud.msghub.example_df123e3ihuhfdaXXXXX",
"redirect_url": "https://example.com",
"image": "https://cdn.com/examples/image.jpg"
},
"priority": 1024
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
sc | Sender / Display name | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
push | Push object containing the push parameters. See below for details | object | Yes |
msisdn | Msisdn can be used for fallback if sending push 1 by 1 | string | No |
fallback | Array of fallback texts | array | No |
priority | Priority of the message. Default: 1024 | integer | No |
Push parameters | Description | Type | Mandatory |
---|---|---|---|
title | Title text | string | Yes |
body | Notification text | string | Yes |
uid | the device ID | string | Yes* |
package | Package name (send push to all devices subscribed to this package) | string | Yes* |
redirect_url | URL to redirect to when the user taps the notification | string | No |
image | Image URL | string | No |
DLR Request Body
Example JSON (DLR)
{
"service_id": "1",
"sms_id": "light-5b83e6df35fe41.14520017"
}
Parameter | Description | Type | Mandatory |
---|---|---|---|
service_id | LINK Mobility Service ID | integer | Yes |
sms_id | Valid LINK Mobility SMS ID (msg_id) | string | Yes |
Response
Response from the API is JSON encoded string. It contains meta and data field.
The meta field contains status code and text description.
The data field contains additional information about the request, for example the sms_id.
Response Status Codes
Code | Description |
---|---|
200 | OK |
400 | Bad Request |
401 | Unauthorized |
402 | Empty required parameters |
403 | Invalid input parameters |
404 | Invalid or empty msisdn |
405 | Invalid or empty service_id |
406 | Invalid or empty text |
407 | Invalid or empty shortcode |
408 | Throttle limit reached |
410 | Empty or invalid ButtonUrl (Viber only) |
411 | Empty or invalid ImageUrl! (Viber only) |
412 | Invalid callback url |
413 | Invalid context data |
414 | Invalid max_sms_size |
415 | SMS with duplicate ID |
417 | Wrong parameter value |
418 | Button name must not exceed 30 characters! |
425 | Invalid survey option |
426 | Invalid carousel item |
427 | Carousel title must be from 2 to 38 characters |
500 | Error processing request |
501 | Send text failed |
Send Response Body
Example send Response
{
"meta": {
"code": 200,
"text": "OK"
},
"data": {
"msg_id": "light-5b83e6df35fe41.14520017",
"sms_id": "light-5b83e6df35fe41.14520017",
"request_id": "605aff94c0fde9.89437771"
}
}
{
"meta": {
"code": 405,
"text":"Invalid or empty service_id"
},
"data": {
"request_id":"6888e8afc77d35.99513458"
}
}
meta | Description | Type | Mandatory |
---|---|---|---|
code | status code | integer | Yes |
text | status description | string | Yes |
data | Description | Type | Mandatory |
---|---|---|---|
sms_id | LINK Mobility SMS ID | string | No* (only on success) |
msg_id | LINK Mobility message ID | string | No* (only on success) |
request_id | The ID of the request | string | Yes |
DLR Response Body
Example dlr Response
{
"meta": {
"code": 200,
"text": "OK"
},
"data": {
"sms_id": "light-5b83e6df35fe41.14520017",
"msg_id": "light-5b83e6df35fe41.14520017",
"status": 202,
"status_msg": "delivered, done date:2022-07-25 12:55:23",
"sent_timestamp": "2022-07-25 12:55:16",
"dlr_timestamp": "2022-07-25 12:55:23",
"request_id": "88350b7cf30e6.37506552"
}
}
{
"meta": {
"code": 200,
"text": "OK"
},
"data": {
"sms_id": "light-5b83e6df35fe41.14520017",
"msg_id": "light-5b83e6df35fe41.14520017",
"status": 202,
"status_msg": "seen, done date:2022-07-25 12:55:34",
"sent_timestamp": "2022-07-25 12:55:16",
"dlr_timestamp": "2022-07-25 12:55:34",
"request_id": "76320b3cf30e6.42512532"
}
}
{
"meta": {
"code": 200,
"text": "OK"
},
"data": {
"sms_id": "light-62de69304938a8.88127029",
"msg_id": "light-62de69304938a8.88127029",
"status": 203,
"status_msg": "bad data, done date:2022-07-25 12:58:14",
"sent_timestamp": "2022-07-25 12:58:13",
"dlr_timestamp": "2022-07-25 12:58:14",
"request_id": "92420b3cf30e6.32512532"
}
}
meta | Description | Type | Mandatory |
---|---|---|---|
code | status code | integer | Yes |
text | status description | string | Yes |
data | Description | Type | Mandatory |
---|---|---|---|
sms_id | LINK Mobility message ID | string | Yes |
msg_id | LINK Mobility message ID | string | Yes |
status | DLR status | integer | Yes |
status_msg | Message for the DLR event (comma separated) | string | Yes |
sent_timestamp | When message was sent | datetime | Yes |
dlr_timestamp | When this DLR event was received | datetime | Yes |
request_id | The ID of the dlr check request | string | Yes |
DLR Statuses
Code | Description |
---|---|
201 | The message is sent to carrier |
202 | The message is delivered to handset |
203 | The message is undelivered (invalid number, queue full, etc.) |
204 | Sending message failed |
205 | The message is rejected from carrier |
206 | Unknown error occurred. Please report this to LINK Mobility. |
Viber Success SubStatuses
Value | Description |
---|---|
delivered | The viber message is received by the device |
seen | The viber message is seen by the user |
Push Notification Success SubStatuses
Value | Description |
---|---|
delivered | The PN is received by the device |
opened | The PN is tapped (seen) by the user |
swiped | The PN is cleared/discarded by the user |
Delivery report push
Example JSON (pushed DLR)
{
"status": 202,
"status_msg": "003: Message is delivered to recipient, submit date: 2022-07-25 12:25:20, done date:2022-07-25 12:25:24",
"sent_timestamp":"2022-07-25 12:25:20",
"dlr_timestamp":"2022-07-25 12:25:24",
"sms_id":"light-5b83e6df35fe41.14523211",
"service_id":"1",
"type":"DLR",
"channel":"SMS"
}
{
"status": 202,
"status_msg": "seen, done date:2022-07-25 12:55:34",
"sent_timestamp":"2022-07-25 12:55:16",
"dlr_timestamp":"2022-07-25 12:55:34",
"sms_id":"light-5b83e6df35fe41.14520017",
"service_id":"1",
"type":"DLR",
"channel":"VIBER"
}
Delivery reports can also be pushed to 3rd party URL using POST.
The URL can be set by LinkMobility
or the client can use the provided callback_url
parameter.
parameters | Description | Type | Mandatory |
---|---|---|---|
status | DLR status | integer | Yes |
status_msg | Message for the DLR event (comma separated) | string | Yes |
sent_timestamp | When message was sent | datetime | Yes |
dlr_timestamp | When this DLR event was received | datetime | Yes |
sms_id | LINK Mobility message ID | string | Yes |
service_id | LINK Mobility Service ID | string | Yes |
type | Event type (DLR) | string | Yes |
channel | The channel of the DLR (SMS/VIBER) | string | Yes |
Headers
POST /send HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 75
Content-Type: application/json
Host: api-test.msghub.cloud
x-api-key: $vY$aQLmRIRHI0eZR6vDay5KYm3iTwCYPOkJfjqmjomtnBztDRZqfpV8oirf
x-api-sign: PlBIoNRG5PFdnfb6AoJgT78LlkIYhqPxS4zvjM0Ell3BtCC9Sebtpq2bPYPR3wowtxvPe1Zn6QBT6
Parameter | Value | Type | Mandatory |
---|---|---|---|
Content-type | application/json | string | Yes |
x-api-key | YOUR_API_KEY | string | Yes |
x-api-sign | HASH HMAC signature | string | Yes |
Expect | string | Yes |
Authentication
Creating a signature
<?php
$signature = hash_hmac('sha512', $postData, $api_secret);
private static string HashHmacSha512(string message, string secret)
{
Encoding encoding = Encoding.UTF8;
using (HMACSHA512 hmac = new HMACSHA512(encoding.GetBytes(secret)))
{
var msg = encoding.GetBytes(message);
var hash = hmac.ComputeHash(msg);
return BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
}
}
import hmac
import hashlib
signature = hmac.new(bytes(API_SECRET, 'utf8'), bytes(data, 'utf8'), hashlib.sha512).hexdigest()
const crypto = require('crypto');
let signature = crypto.createHmac("sha512", secret).update(data).digest('hex');
private static String buildHmacSignature(String value, String secret) {
String result;
try {
Mac hmacSHA512 = Mac.getInstance("HmacSHA512");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(),
"HmacSHA512");
hmacSHA512.init(secretKeySpec);
byte[] digest = hmacSHA512.doFinal(value.getBytes());
BigInteger hash = new BigInteger(1, digest);
result = hash.toString(16);
if ((result.length() % 2) != 0) {
result = "0" + result;
}
} catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException ex) {
throw new RuntimeException("Problem calculating HMAC", ex);
}
return result;
}
require 'openssl'
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha512'), API_SECRET, data)
package main
import (
"crypto/hmac"
"crypto/sha512"
"encoding/hex"
)
h := hmac.New(sha512.New, []byte(Api_secret))
h.Write(dataJson)
signature := hex.EncodeToString(h.Sum(nil))
To authenticate to the API you have to sign the data you post.
Construct the Request Body in JSON format
Create a hash using the shared api_secret
Include the signature in the request headers as x-api-sign
Using the shared api_secret, LINK Mobility creates signature of the received JSON POST data
If LINK Mobility’s signature matches the one included in the request, the request is serviced
Examples
<?php
/**
* Single Message
*/
$api_endpoint = "https://<LINK_MOBILITY_ENDPOINT>/send";
$api_key = '<YOUR_API_KEY>';
$api_secret = '<YOUR_API_SECRET>';
$data = [
'msisdn' => '35988XXXXXXX',
'sc' => '1909',
'text' => 'Test Message!',
'service_id' => '<YOUR_SERVICE_ID>',
];
$data = json_encode($data);
$signature = hash_hmac('sha512', $data, $api_secret);
$headers = [
"Content-Type: application/json",
"x-api-key: {$api_key}",
"x-api-sign: {$signature}",
"Expect: ",
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_endpoint);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$result = curl_exec($ch);
curl_close($ch);
$result = json_decode($result);
var_dump($result);
/**
* Multiple Messages
*/
$api_endpoint = "https://<LINK_MOBILITY_ENDPOINT>/send";
$api_key = '<YOUR_API_KEY>';
$api_secret = '<YOUR_API_SECRET>';
$data = [
[
'msisdn' => '35988XXXXXXX',
'sc' => '1909',
'text' => 'Test Message!',
'service_id' => '<YOUR_SERVICE_ID>',
],
[
'msisdn' => '35988XXXXXXX',
'sc' => '1909',
'text' => 'Test Message!',
'service_id' => '<YOUR_SERVICE_ID>',
],
[
'msisdn' => '35988XXXXXXX',
'sc' => '1909',
'text' => 'Test Message!',
'service_id' => '<YOUR_SERVICE_ID>',
],
];
$data = json_encode($data);
$signature = hash_hmac('sha512', $data, $api_secret);
$headers = [
"Content-Type: application/json",
"x-api-key: {$api_key}",
"x-api-sign: {$signature}",
"Expect: ",
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_endpoint);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$result = curl_exec($ch);
curl_close($ch);
$result = json_decode($result);
var_dump($result);
using System;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Security.Cryptography;
/**
* Single message
*/
namespace SMS
{
class Program
{
private const string apiEndPoint = "https://<LINK_MOBILITY_ENDPOINT>/send";
private const string apiKey = "<YOUR_API_KEY>";
private const string apiSecret = "<YOUR_API_SECRET>";
private class SMSRequestData
{
public string msisdn { get; set; }
public string sc { get; set; }
public string text { get; set; }
public string service_id { get; set; }
}
static async Task Main(string[] args)
{
SMSRequestData data = new SMSRequestData
{
msisdn = "35988XXXXXXX",
sc = "1909",
text = "Test Message!",
service_id = "<YOUR_SERVICE_ID>",
};
string jsonData = JsonSerializer.Serialize(data);
string signature = HashHmacSha512(jsonData, apiSecret);
var postData = new StringContent(jsonData, Encoding.UTF8, "application/json");
using var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
client.DefaultRequestHeaders.Add("x-api-sign", signature);
client.DefaultRequestHeaders.Add("Expect", string.Empty);
var response = await client.PostAsync(apiEndPoint, postData);
string result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
}
private static string HashHmacSha512(string message, string secret)
{
Encoding encoding = Encoding.UTF8;
using (HMACSHA512 hmac = new HMACSHA512(encoding.GetBytes(secret)))
{
var msg = encoding.GetBytes(message);
var hash = hmac.ComputeHash(msg);
return BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
}
}
}
}
/**
* Multiple messages
*/
namespace SMS
{
class Program
{
private const string apiEndPoint = https://<LINK_MOBILITY_ENDPOINT>/send;
private const string apiKey = "<YOUR_API_KEY>";
private const string apiSecret = "<YOUR_API_SECRET>";
private class SMSRequestData
{
public string msisdn { get; set; }
public string sc { get; set; }
public string text { get; set; }
public string service_id { get; set; }
}
static async Task Main(string[] args)
{
List<SMSRequestData> smsRequests = new List<SMSRequestData>()
{
new SMSRequestData
{
msisdn = "35988XXXXXXX",
sc = "1909",
text = "Test Message!",
service_id = "<YOUR_SERVICE_ID>",
},
new SMSRequestData
{
msisdn = "35988XXXXXXX",
sc = "1909",
text = "Test Message!",
service_id = "<YOUR_SERVICE_ID>",
}
new SMSRequestData
{
msisdn = "35988XXXXXXX",
sc = "1909",
text = "Test Message!",
service_id = "<YOUR_SERVICE_ID>",
}
};
string jsonData = JsonSerializer.Serialize(smsRequests);
string signature = HashHmacSha512(jsonData, apiSecret);
var postData = new StringContent(jsonData, Encoding.UTF8, "application/json");
using var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
client.DefaultRequestHeaders.Add("x-api-sign", signature);
client.DefaultRequestHeaders.Add("Expect", string.Empty);
var response = await client.PostAsync(apiEndPoint, postData);
string result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
}
private static string HashHmacSha512(string message, string secret)
{
Encoding encoding = Encoding.UTF8;
using (HMACSHA512 hmac = new HMACSHA512(encoding.GetBytes(secret)))
{
var msg = encoding.GetBytes(message);
var hash = hmac.ComputeHash(msg);
return BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
}
}
}
}
import hmac
import hashlib
import json
import requests
#
# Single message
#
ENDPOINT = "https://<LINK_MOBILITY_ENDPOINT>/send"
API_KEY = "<YOUR_API_KEY>"
API_SECRET = "<YOUR_API_SECRET>"
data = {
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "1"
};
data = json.dumps(data)
signature = hmac.new(bytes(API_SECRET, 'utf8'), bytes(data, 'utf8'), hashlib.sha512).hexdigest()
headers = {
"Content-Type": "application/json",
"x-api-key": API_KEY,
"x-api-sign": signature,
"Expect": ""
};
request = requests.post(ENDPOINT, data=data, headers=headers)
print(request.text)
#
# Multiple messages
#
ENDPOINT = "https://<LINK_MOBILITY_ENDPOINT>/send"
API_KEY = "<YOUR_API_KEY>"
API_SECRET = "<YOUR_API_SECRET>"
data = [
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "1"
},
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "1"
},
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "1"
}
]
data = json.dumps(data)
signature = hmac.new(bytes(API_SECRET, 'utf8'), bytes(data, 'utf8'), hashlib.sha512).hexdigest()
headers = {
"Content-Type": "application/json",
"x-api-key": API_KEY,
"x-api-sign": signature,
"Expect": ""
};
request = requests.post(ENDPOINT, data=data, headers=headers)
print(request.text)
const crypto = require('crypto');
const request = require('request');
const endpoint = "https://<LINK_MOBILITY_ENDPOINT>/send";
const api_key = "<YOUR_API_KEY>";
const secret = "<YOUR_API_SECRET>";
/**
* Single message
*/
let data = {
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "<YOUR_SERVICE_ID>",
};
data = JSON.stringify(data);
let signature = crypto.createHmac("sha512", secret).update(data).digest('hex');
let headers = {
"Content-Type": "application/json",
"x-api-key": `${api_key}`,
"x-api-sign": `${signature}`,
"Expect": "",
};
const callback = (error, response, body) => {
console.log(body);
};
request.post(endpoint, {
headers: headers,
body: data,
callback
});
/**
* Multiple messages
*/
let data = [
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "<YOUR_SERVICE_ID>",
},
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "<YOUR_SERVICE_ID>",
},
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test Message!",
"service_id": "<YOUR_SERVICE_ID>",
}
];
data = JSON.stringify(data);
let signature = crypto.createHmac("sha512", secret).update(data).digest('hex');
let headers = {
"Content-Type": "application/json",
"x-api-key": `${api_key}`,
"x-api-sign": `${signature}`,
"Expect": "",
};
const callback = (error, response, body) => {
console.log(body);
};
request.post(endpoint, {
headers: headers,
body: data,
callback
});
/**
* Single message
*/
public class Main {
private static String buildHmacSignature(String value, String secret) {
String result;
try {
Mac hmacSHA512 = Mac.getInstance("HmacSHA512");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(),"HmacSHA512");
hmacSHA512.init(secretKeySpec);
byte[] digest = hmacSHA512.doFinal(value.getBytes());
BigInteger hash = new BigInteger(1, digest);
result = hash.toString(16);
if ((result.length() % 2) != 0) {
result = "0" + result;
}
} catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException ex) {
throw new RuntimeException("Problem calculating HMAC", ex);
}
return result;
}
public static void main(String[] args) throws IOException, InterruptedException {
final String ENDPOINT = "https://<LINK_MOBILITY_ENDPOINT>/send";
final String API_KEY = "<YOUR_API_KEY>";
final String API_SECRET = "<YOUR_API_SECRET>";
String data = "{" +
"\"msisdn\":\"35988XXXXXXX\"," +
"\"sc\":\"1909\"," +
"\"text\":\"Test message!\"," +
"\"service_id\":\"<YOUR_SERVICE_ID>\"" +
"}";
String signature = buildHmacSignature(data, API_SECRET);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(ENDPOINT))
.POST(HttpRequest.BodyPublishers.ofString(data))
.header("Content-Type", "application/json")
.header("x-api-key", API_KEY)
.header("x-api-sign", signature)
/*.header("Expect", "")*/ // must be run with java -Djdk.httpclient.allowRestrictedHeaders=expect
.build();
HttpResponse<String> response = client.send(request,HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
/**
* Multiplce messages
*/
public class Main {
private static String buildHmacSignature(String value, String secret) {
String result;
try {
Mac hmacSHA512 = Mac.getInstance("HmacSHA512");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(),"HmacSHA512");
hmacSHA512.init(secretKeySpec);
byte[] digest = hmacSHA512.doFinal(value.getBytes());
BigInteger hash = new BigInteger(1, digest);
result = hash.toString(16);
if ((result.length() % 2) != 0) {
result = "0" + result;
}
} catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException ex) {
throw new RuntimeException("Problem calculating HMAC", ex);
}
return result;
}
public static void main(String[] args) throws IOException, InterruptedException {
final String ENDPOINT = "https://<LINK_MOBILITY_ENDPOINT>/send";
final String API_KEY = "<YOUR_API_KEY>";
final String API_SECRET = "<YOUR_API_SECRET>";
JSONArray data = new JSONArray(
"[{\"msisdn\":\"35988XXXXXXX\",\"sc\":\"1909\",\"text\":\"Test message!\",\"service_id\":\"<YOUR_SERVICE_ID>\"},{\"msisdn\":\"35988XXXXXXX\",\"sc\":\"1909\",\"text\":\"Test message!\",\"service_id\":\"<YOUR_SERVICE_ID>\"},{\"msisdn\":\"35988XXXXXXX\",\"sc\":\"1909\",\"text\":\"Test message!\",\"service_id\":\"<YOUR_SERVICE_ID>\"}]"
);
String signature = buildHmacSignature(data, API_SECRET);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(ENDPOINT))
.POST(HttpRequest.BodyPublishers.ofString(data))
.header("Content-Type", "application/json")
.header("x-api-key", API_KEY)
.header("x-api-sign", signature)
/*.header("Expect", "")*/ // must be run with java -Djdk.httpclient.allowRestrictedHeaders=expect
.build();
HttpResponse<String> response = client.send(request,HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
require 'json'
require 'openssl'
require 'net/https'
#
# Single message
#
ENDPOINT = "https://<LINK_MOBILITY_ENDPOINT>/send"
API_KEY = "<YOUR_API_KEY>"
API_SECRET = "<YOUR_API_SECRET>"
data = {
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test message!",
"service_id": "<YOUR_SERVICE_ID>"
};
data = data.to_json
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha512'), API_SECRET, data)
headers = {
"Content-Type": "application/json",
"x-api-key": API_KEY,
"x-api-sign": signature,
"Expect": ""
};
uri = URI.parse(ENDPOINT)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.body = data
http.use_ssl = true
response = http.request(request)
puts response.body
#
# Multiple messages
#
ENDPOINT = "https://<LINK_MOBILITY_ENDPOINT>/send"
API_KEY = "<YOUR_API_KEY>"
API_SECRET = "<YOUR_API_SECRET>"
data = [
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test message!",
"service_id": "<YOUR_SERVICE_ID>"
},
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test message!",
"service_id": "<YOUR_SERVICE_ID>"
},
{
"msisdn": "35988XXXXXXX",
"sc": "1909",
"text": "Test message!",
"service_id": "<YOUR_SERVICE_ID>"
}
];
data = data.to_json
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha512'), API_SECRET, data)
headers = {
"Content-Type": "application/json",
"x-api-key": API_KEY,
"x-api-sign": signature,
"Expect": ""
};
uri = URI.parse(ENDPOINT)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.body = data
http.use_ssl = true
response = http.request(request)
puts response.body
package main
/**
* Single message
*/
import (
"bytes"
"crypto/hmac"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
const (
Endpoint = "https://<LINK_MOBILITY_ENDPOINT>/send"
Api_key = "<YOUR_API_KEY>"
Api_secret = "<YOUR_API_SECRET>"
)
dataMap := map[string]string{
"msisdn": "35988XXXXXXX",
"sc": "1990",
"text": "Test message!",
"service_id": "<YOUR_SERVICE_ID>",
}
dataJson, _ := json.Marshal(dataMap)
h := hmac.New(sha512.New, []byte(Api_secret))
h.Write(dataJson)
signature := hex.EncodeToString(h.Sum(nil))
client := &http.Client{}
req, _ := http.NewRequest("POST", Endpoint, bytes.NewBuffer(dataJson))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("x-api-key", Api_key)
req.Header.Add("x-api-sign", signature)
req.Header.Add("Expect", "")
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
/**
* Multiple messages
*/
import (
"bytes"
"crypto/hmac"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
)
type message struct {
MSISDN string `json:"msisdn"`
ShortCode string `json:"sc"`
Text string `json:"text"`
ServiceId string `json:"service_id"`
}
func main() {
dataMap := []message{{
"3598888888888",
"1990",
"test 1",
"12345",
},
{
"3598888811111",
"1900",
"test 2",
"4567",
}}
payload, _ := json.Marshal(dataMap)
h := hmac.New(sha512.New, []byte(Api_secret))
h.Write(payload)
signature := hex.EncodeToString(h.Sum(nil))
client := &http.Client{}
req, _ := http.NewRequest("POST", Endpoint, bytes.NewBuffer(payload))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("x-api-key", Api_key)
req.Header.Add("x-api-sign", signature)
req.Header.Add("Expect", "")
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Using Postman
Signature generation in Postman (Pre-request script)
API_KEY = 'YOUR_API_KEY';
SECRET_KEY = 'YOUR_API_SECRET';
function getAuthHeaderxkey(requestBody) {
var hmacDigest = CryptoJS.HmacSHA512(requestBody, SECRET_KEY).toString(CryptoJS.digest);
return hmacDigest;
}
var Header = require('postman-collection').Header
pm.request.headers.add(new Header("x-api-key: " + API_KEY))
pm.request.headers.add(new Header("x-api-sign: " + getAuthHeaderxkey(request['data'])))
You can download Postman collection from this url: download
The collection includes examples for:
- Testing endpoint for /send method
- Testing endpoint /dlr method
- Production endpoint /send method
- Production endpoint /dlr method
Message Priority
Example using custom high priority
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"priority": "101"
}
Example using predefined priority constant
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"priority": "high"
}
Priority can be added to each message.
The priority range is from 0 to 2048.
Priority can also be set with the predefined constants: low, high, normal;
Predefined constant | Value |
---|---|
low | 2048 |
normal | 1024 |
high | 512 |
Message Delay
Example delay with 5 minutes
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"delay": "300"
}
Example using mnemonic
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"delay": "+10 minutes"
}
The message delay defines when the message should be send.
The delay value is in seconds, but you can also use mnemonics.
Example mnemonics:
1 November 2018 10:00:00
+30 minutes
+10 seconds
2019-10-02 09:00:00
Unique Parameter
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"unique": true
}
When unique parameter is set to true the API will check if currently send message has already been sent and reply with error if so.
The API uses parent_msg_id as unique identifier for the message.
If not provided it will create one using combination of msisdn, sc, text.
Message Size
The maximum SMS size depends on message encoding. The encoding depends on the characters used in content.
The default encoding for SMS messages is GSM-7 bit. This encoding is limited to 140 bytes or total of 160 characters. For characters supported by GSM-7 bit please refer to this link.
UCS-2 encoding (Unicode) supports up to 70 16 bit characters. e.g. Cyrillic, Chinese, etc.
Sending longer messages will be sent as concatenated message and billed as more than one SMS as per the table below:
Number of SMS | Maximum GSM-7 characters | Maximum Unicode characters |
---|---|---|
1 | 160 | 70 |
2 | 306 | 134 |
3 | 459 | 201 |
4 | 612 | 268 |
5 | 765 | 335 |
6 | 918 | 402 |
7 | 1071 | 469 |
8 | 1224 | 536 |
9 | 1377 | 603 |
Concatenated Message
Example of limiting maximum messages to send to 1
{
"msisdn": "359XXXXXXXXX",
"sc": "1909",
"text": "Test SMS Message",
"service_id": "1",
"max_sms_size": 1
}
Concatenated message is a long message (more than 160 characters in 7 bit encoding or more than 70 in UCS-2 encoding).
These messages are split and send individually to the Mobile Operator. (additional charges may occur)
Once received on the end user handset they are recombined again into 1 message.
SMS Fallback
Example Fallback
{
"msisdn": "359XXXXXXXXX",
"sc": "ViberTest",
"text": "Test Viber Message",
"service_id": "1",
"fallback": {
"sms": "Test Message SMS"
}
}
Fallback is functionality that will resend failed Viber messages as SMS.
Two Way
MO Push
Example JSON (MO push)
{
"msisdn": "35989XXXXXXX",
"sc": "1513",
"text": "Test",
"service_id": "1",
"mno_request_dt": "2022-07-25 12:55:16.77755",
"mcc": "284",
"mnc": "1",
"sms_id": "BGP1-5ba0754b307f73.64142531",
"type": "MO"
}
MO stands for mobile originated. This is the message send from end-user mobile phone.
LINK Mobility provides 3rd parties with api_key, api_secret, service_id and list of allowed shortcodes. 3rd party should provide endpoint where LINK Mobility should push MO messages.
LINK Mobility pushes these messages to 3rd party endpoint in JSON format.
parameters | Description | Type | Mandatory |
---|---|---|---|
msisdn | End-user MSISDN | string | Yes |
sc | Sender / Display name | string | Yes |
text | The sent text message | string | Yes |
service_id | LINK Mobility Service ID | integer | Yes |
mno_request_dt | When MO message was sent | datetime | No |
mcc | Mobile Country Code | integer | No |
mnc | Mobile Network Code | integer | No |
sms_id | LINK Mobility message ID | string | Yes |
type | Type of message | string | Yes |
Headers
3rd party can use x-api-sign hash to verify the message integrity.
Parameter | Value | Type | Mandatory |
---|---|---|---|
Content-type | application/json | string | Yes |
x-api-sign | Hash Signature with 3rd party api_secret | string | Yes |
Response
Example JSON (no replay)
{
"meta": {
"code": 200,
"text": "OK"
},
"data": {
}
}
Example JSON (replay)
{
"meta": {
"code": 200,
"text": "OK"
},
"data": {
"text": "Thank you for your message!"
}
}
3rd party response should also be in JSON format. It must contain meta and data field
3rd party can reply to the MO message synchronous using the data param text. The provided text will be send back to the end-user.
meta | Description | Type | Mandatory |
---|---|---|---|
code | status code | integer | Yes |
text | status description | string | Yes |
data | Description | Type | Mandatory |
---|---|---|---|
text | Replay text | string | No |
Response Codes
Code | Description |
---|---|
200 | OK |
400 | Bad Request |
500 | Error processing request |
MSISDN encryption
Example MSISDN decryption
<?php
$msisdn = base64_decode($encrypted_msisdn);
$iv_length = openssl_cipher_iv_length('aes-256-cbc');
$iv = substr($msisdn, 0, $iv_length);
$msisdn = substr($msisdn, $iv_length);
$msisdn = openssl_decrypt($msisdn, 'aes-256-cbc', $api_secret, 0, $iv);
MSISDN can be encrypted using 3rd party api_secret provided by LINK Mobility.
MSISDN encryption is setting up on service provisioning.
Refer to example for decryption.