SMS TCP API¶
The TCP transport addresses a module method by command name. It is the entry point for server-to-server integrations: CRM backends, notification services, campaign jobs, and anything that already holds a persistent platform connection.
The same methods are available over HTTP — see REST API. The command name and the HTTP path are two addresses of one method, with identical fields and results.
Request envelope¶
{
"command": "SmsSendMessage",
"extID": "8f2a1c",
"data": {
"sendToParentType": "CUSTOMER",
"sendToParentId": 140,
"phone": "+380951234567",
"message": "Your code is 4821"
}
}
| Field | Type | Description |
|---|---|---|
command |
string | Method name, exactly as published by the module |
extID |
string | Caller-generated correlation id; the response carries it back |
data |
object | Method parameters |
extID is what makes the connection full-duplex: responses and streamed events share one
socket, so a reply is matched to its request by extID rather than by arrival order.
Response¶
{
"extID": "8f2a1c",
"status": 200,
"data": {
"smsId": 812,
"status": "PENDING",
"parts": 1,
"scheduleReason": "IMMEDIATE"
}
}
An error keeps the same envelope: the outcome is in status, and data carries the
message.
{
"extID": "8f2a1c",
"status": 409,
"data": "Phone 380950000000 is blacklisted: opt-out"
}
Status codes are the same as in REST. 502 means the module itself
is not on the bus — the platform answers instead of hanging the request. A malformed
request never reaches the module and comes back as 400 with
{"error": "NO_COMMAND_OR_EXT_ID"} or {"error": "INVALID_JSON_OBJECT"}.
SmsSendMessage answers as soon as the message is queued, not when it is delivered.
The delivery outcome arrives later as an event.
Node.js SDK¶
scaletrade-server-api exposes any
published command as a method, so no client update is needed when a module adds methods:
const STPlatform = require('scaletrade-server-api');
const platform = new STPlatform(
'broker.scaletrade.com:8080',
'notification-service',
{},
null, null,
'your-jwt-auth-token'
);
const sms = await platform.SmsSendMessage({
sendToParentType: 'CUSTOMER',
sendToParentId: 140,
phone: '+380951234567',
message: 'Your code is 4821',
kind: 'TRANSACTIONAL',
idempotencyKey: 'login-code-140-1755000000'
});
if (sms.status === 200) {
console.log('queued', sms.data.smsId, sms.data.parts, 'segment(s)');
} else {
// On failure `data` is the message text, not an object.
console.error(sms.status, sms.data);
}
idempotencyKey matters for exactly this caller: a retried HTTP request or a restarted job
must not send the customer a second confirmation code. Repeating the call with the same key
returns the original message with idempotent: true.
Commands¶
Messages¶
| Command | Description |
|---|---|
| SmsSendMessage | Queue a message with an explicit text |
| SmsSendFromTemplate | Queue a message rendered from a template |
| SmsSendBulk | Queue up to 10 000 recipients as one campaign |
| SmsResendMessage | Send a copy of an existing message |
| SmsCancelMessage | Cancel a message still in the queue |
| SmsPreviewMessage | Text, encoding and segments without sending |
| SmsGetMessage | One message with attempts and webhook events |
| SmsGetMessages | Message history with filters and aggregates |
| SmsGetStats | Volume, segments, spend and delivery rate |
| SmsGetLastMessagesByParents | Last message time for a batch of recipients |
Templates and triggers¶
| Command | Description |
|---|---|
| SmsAddTemplate | Create a template |
| SmsUpdateTemplate | Update a template |
| SmsCloneTemplate | Copy a template |
| SmsDeleteTemplate | Delete a template |
| SmsGetTemplates | Templates of the brand |
| SmsAddTrigger | Subscribe an event to templates |
| SmsUpdateTrigger | Update a trigger |
| SmsDeleteTrigger | Delete a trigger |
| SmsFireTrigger | Fire an event by hand |
| SmsGetTriggers | Triggers of the brand |
| SmsGetTriggerSubscriptions | What the module listens to |
Providers, profiles and agents¶
| Command | Description |
|---|---|
| SmsGetProviderAdapters | Implemented adapters and capabilities |
| SmsAddProvider | Register a provider |
| SmsUpdateProvider | Update a provider |
| SmsDeleteProvider | Soft-delete a provider |
| SmsGetProviders | Provider catalogue |
| SmsAddProfile | Create a provider profile |
| SmsUpdateProfile | Update a provider profile |
| SmsDeleteProfile | Soft-delete a provider profile |
| SmsGetProfiles | Provider profiles of the brand |
| SmsGetMyProfiles | Profiles available to the current manager |
| SmsGetProfileBalance | Provider balance and low-balance flags |
| SmsAddAgent | Link a manager to a sender identity |
| SmsUpdateAgent | Update the link |
| SmsDeleteAgent | Remove the link |
| SmsGetAgents | Links of the brand |
Blacklist, send windows, webhooks, service¶
| Command | Description |
|---|---|
| SmsAddBlacklistPhone | Blacklist a number |
| SmsImportBlacklistPhones | Bulk import numbers |
| SmsDeleteBlacklistPhone | Remove a number |
| SmsCheckBlacklistPhone | Check one number |
| SmsGetBlacklist | Blacklist entries |
| SmsAddSendWindow | Add a send window |
| SmsUpdateSendWindow | Update a send window |
| SmsDeleteSendWindow | Delete a send window |
| SmsCheckSendWindow | Whether a number can be messaged now |
| SmsGetSendWindows | Send windows |
| SmsAddWebhook | Create a delivery-report URL |
| SmsUpdateWebhook | Update a webhook |
| SmsDeleteWebhook | Delete a webhook |
| SmsGetWebhooks | Webhooks of the brand |
| SmsGetWebhookEvents | Raw provider events |
| SmsHandleWebhook | Public provider endpoint (HTTP only in practice) |
| SmsPing | Liveness probe with a problem list |
| SmsHealth | Full module state |
Events¶
Message progress is delivered as events on the same connection, so an integration does not
poll SmsGetMessages:
| Event | When |
|---|---|
sms.message.queued |
Message accepted into the queue |
sms.message.sent |
Provider accepted the message |
sms.message.final |
Delivered, undelivered, rejected, expired or failed |
platform.emitter.on('sms.message.final', e => {
console.log(e.data.smsId, e.data.status, e.data.price);
});
A message whose provider never reports a final status is closed by the module as EXPIRED,
so the event always arrives and a waiting integration cannot hang forever.
Monitoring the module¶
SmsPing is the probe to call from a monitor: it answers ok plus a problems list and
costs one SELECT 1. A process that answers on the bus can still have lost its database or
its queue cron, and in that state messages pile up in PENDING while nothing is sent — an
empty problems list is what actually means healthy.