gRPC Messaging
SMS Service
Messaging service responsible for SMS submission and message lifecycle management.
For authentication details (JWT tokens, metadata headers, error codes), see gRPC Authentication.
Service name:
sms.v1.SmsService
SendMessage
Sends a single SMS message (unary RPC).
rpc SendMessage(SmsRequest) returns (SmsResponse)
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
task_id | int64 | No | Client-defined task identifier for grouping messages (e.g., campaign ID) |
transaction_id | string | No | Client-defined UUID for end-to-end message tracing. If omitted, the platform generates one |
app_id | int64 | Yes | Application identifier (must match app_id in metadata) |
esme | int64 | Yes | Target SMPP link ID or pool ID. Determines the routing destination |
ston | int32 | No | Source TON (Type of Number). Default: 0 (Unknown) |
snpi | int32 | No | Source NPI (Numbering Plan Indicator). Default: 0 (Unknown) |
saddr | string | Yes | Source address (sender ID) — phone number or alphanumeric string |
dton | int32 | No | Destination TON. Default: 1 (International) |
dnpi | int32 | No | Destination NPI. Default: 1 (ISDN/E.164) |
daddr | string | Yes | Destination phone number in international format (e.g., 998901234567) |
message | string | Yes | SMS text content. Long messages are automatically segmented |
sent_at | int64 | No | Unix timestamp indicating when the message was originally created |
strategy | string | No | MCP contact policy strategy to apply: cp1, onbuild_cp1, cp2, blacklist. If omitted, no contact policy is applied |
{
"task_id": 11,
"transaction_id": "550e8400-e29b-41d4-a716-446655440000",
"app_id": 1,
"esme": 1000,
"ston": 0,
"snpi": 5,
"saddr": "MYBRAND",
"dnpi": 1,
"dton": 1,
"daddr": "998901234567",
"message": "Hello World",
"sent_at": 1937082918,
"strategy": "cp1"
}
Response Fields
| Field | Type | Description |
|---|---|---|
success | bool | true if the message was accepted for delivery |
error | string | Error description if submission failed, empty string on success |
esme | int64 | SMPP link ID that was used for routing (may differ from request if a pool was specified) |
transaction_id | string | Transaction identifier for end-to-end tracing |
message_id | string | Platform-generated message identifier for delivery tracking |
status | string | Submission status: submitted, or MCP rejection reason (daily_cp, weekly_cp, duplicate, blacklist) |
sms_ids | repeated string | SMSC-assigned IDs for each message segment. Multiple IDs indicate the message was split into parts |
task_id | int64 | Echoed task identifier from the request |
{
"success": true,
"error": "",
"esme": 1000,
"transaction_id": "550e8400-e29b-41d4-a716-446655440000",
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "submitted",
"sms_ids": [
"smsc-seg-001"
],
"task_id": 11
}
Processing Pipeline
This method performs the full SMS processing pipeline:
- Authentication — validates JWT token and app_id
- Rate limiting — checks L1 (in-process) and L2 (Redis) limits
- Contact policy — validates against MCP strategy (if specified)
- SMPP routing — selects target link or load-balances across pool
- Submission — sends message to SMSC over SMPP v3.4
- Persistence — stores message state in Aerospike for delivery tracking
grpcurl Example
grpcurl \
-plaintext \
-H 'authorization: Bearer <jwt-token>' \
-H 'app_id: 1' \
-d '{
"app_id": 1,
"esme": 1000,
"saddr": "MYBRAND",
"daddr": "998901234567",
"message": "Hello from gRPC",
"strategy": "cp1"
}' \
127.0.0.1:44044 \
sms.v1.SmsService/SendMessage
SendMessageStream
Bidirectional streaming interface for high-throughput message submission.
rpc SendMessageStream(stream SmsRequest) returns (stream SmsResponse)
Description
Allows clients to maintain a persistent gRPC connection and send multiple SMS messages over a single stream. Each incoming SmsRequest produces a corresponding SmsResponse.
This is the recommended method for high-volume integrations because it eliminates the overhead of establishing a new connection for each message.
When to Use Streaming
| Scenario | Recommended Method |
|---|---|
| Sending a single message | SendMessage (unary) |
| Campaign with hundreds or thousands of messages | SendMessageStream (streaming) |
| Real-time event-driven messaging | SendMessageStream (streaming) |
| Simple integration or testing | SendMessage (unary) |
Request and Response
The request and response messages are the same as SendMessage — SmsRequest and SmsResponse. The only difference is the transport: messages flow continuously in both directions over a single connection.
Example: Go Client
// Establish a streaming connection
stream, err := client.SendMessageStream(ctx)
if err != nil {
log.Fatal(err)
}
// Send messages
for _, msg := range messages {
err := stream.Send(&pb.SmsRequest{
AppId: 1,
Esme: 1000,
Saddr: "MYBRAND",
Daddr: msg.Phone,
Message: msg.Text,
TransactionId: msg.TxID,
Strategy: "cp1",
})
if err != nil {
log.Printf("send error: %v", err)
break
}
// Receive response for each sent message
resp, err := stream.Recv()
if err != nil {
log.Printf("recv error: %v", err)
break
}
log.Printf("message %s: %s", resp.TransactionId, resp.Status)
}
stream.CloseSend()
Example: Python Client
import grpc
import sms_pb2, sms_pb2_grpc
channel = grpc.insecure_channel("127.0.0.1:44044")
stub = sms_pb2_grpc.SmsServiceStub(channel)
metadata = [
("authorization", "Bearer <jwt-token>"),
("app_id", "1"),
]
def message_generator():
for phone, text in messages:
yield sms_pb2.SmsRequest(
app_id=1,
esme=1000,
saddr="MYBRAND",
daddr=phone,
message=text,
strategy="cp1",
)
responses = stub.SendMessageStream(message_generator(), metadata=metadata)
for resp in responses:
print(f"{resp.transaction_id}: {resp.status}")
CheckESME
Checks the availability of a specific SMPP link.
rpc CheckESME(CheckESMERequest) returns (CheckESMEResponse)
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
esme | int64 | Yes | SMPP link ID to check |
{
"esme": 101
}
Response Fields
| Field | Type | Description |
|---|---|---|
persist | bool | true if the SMPP connection is currently active |
system_id | string | SMPP system ID of the connected SMSC |
{
"persist": true,
"system_id": "smpp-client-1"
}
Use this endpoint to verify SMPP connectivity before sending messages. A persist: false response indicates the link is disconnected — the proxy will automatically reconnect when the SMSC becomes available.
CheckTextLen
Analyzes SMS text and calculates encoding and segmentation.
rpc CheckTextLen(CheckTextLenRequest) returns (CheckTextLenResponse)
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Yes | SMS text content to analyze |
{
"text": "Hello World"
}
Response Fields
| Field | Type | Description |
|---|---|---|
error | string | Error description if analysis failed, empty string on success |
encode | string | Detected encoding: GSM7 (standard 7-bit) or UCS2 (Unicode 16-bit) |
symbols | int32 | Number of characters in the message |
parts | int32 | Number of SMS segments required to deliver the message |
{
"error": "",
"encode": "GSM7",
"symbols": 11,
"parts": 1
}
Segmentation Rules
SMS messages have character limits that depend on encoding:
| Encoding | Single SMS | Each Segment (multipart) |
|---|---|---|
| GSM7 (Latin, digits, basic symbols) | 160 characters | 153 characters |
| UCS2 (Cyrillic, Arabic, emoji, CJK) | 70 characters | 67 characters |
When a message exceeds the single SMS limit, it is automatically split into segments. The reduced per-segment limit accounts for the User Data Header (UDH) used for reassembly.
Use this endpoint to preview segmentation before sending — for example, to estimate costs or warn users about multipart messages in a UI.
SmsInfo
Returns delivery information about a previously sent message.
rpc SmsInfo(SmsStatusRequest) returns (SmsStatusResponse)
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
esme | int64 | Yes | SMPP link ID the message was sent through |
message_id | string | Yes | Message identifier returned by SendMessage or SendMessageStream |
{
"esme": 100,
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Response Fields
| Field | Type | Description |
|---|---|---|
message_id | string | Platform message identifier |
mode | string | single for single-segment SMS, multipart for segmented messages |
parts | map<string, string> | Per-segment delivery status. Key is the segment number (1-based), value is the delivery status |
delivered_count | int32 | Number of segments that have been delivered |
sent_at | int64 | Unix timestamp when the message was submitted |
updated_at | int64 | Unix timestamp of the last status update |
transaction_id | string | End-to-end transaction identifier |
task_id | int64 | Client-defined task identifier |
status | string | Overall message status (see table below) |
error | string | Error description if the query failed |
{
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"mode": "single",
"parts": {
"1": "DELIVERED"
},
"delivered_count": 1,
"sent_at": 1937082918,
"updated_at": 1937082925,
"transaction_id": "550e8400-e29b-41d4-a716-446655440000",
"task_id": 11,
"status": "DELIVERED",
"error": ""
}
Message Status Values
| Status | Description |
|---|---|
submitted | Message was sent to the SMSC, waiting for delivery receipt |
DELIVERED | Message was successfully delivered to the recipient |
UNDELIVERABLE | SMSC reported the message could not be delivered |
EXPIRED | Delivery timed out before the SMSC confirmed receipt |
REJECTED | SMSC rejected the message |
gRPC Request Lifecycle
A typical gRPC request follows the same processing pipeline as REST:
Client
↓
Authentication Interceptor (JWT + app_id validation)
↓
L1 Rate Limiter (in-process)
↓
L2 Distributed Rate Limiter (Redis)
↓
Contact Policy (MCP, if strategy is specified)
↓
SMPP Router (link selection or pool load balancing)
↓
SMSC
Each stage validates and processes the message before forwarding it to the next component. If any stage rejects the message, the response is returned immediately with the appropriate error.
Service Summary
| Service | Method | Type | Description |
|---|---|---|---|
| SmsService | SendMessage | Unary | Send a single SMS message |
| SmsService | SendMessageStream | Bidirectional streaming | High-throughput streaming submission |
| SmsService | CheckESME | Unary | Check SMPP link availability |
| SmsService | CheckTextLen | Unary | Analyze text encoding and segmentation |
| SmsService | SmsInfo | Unary | Query message delivery status |