SmsSendBulk¶
POST sms/message/bulk
Queues a campaign: up to 10 000 recipients in one call, either with a shared text or from a template with per-recipient data.
A recipient that cannot be queued — blacklisted, opted out, bad number — does not abort the
campaign. It lands in failed with its reason while the rest is queued, because a
half-imported list is worse than a list with three known gaps.
Access Control¶
Allowed roles: admin, leader. Marketing campaigns are not a per-manager action.
Request¶
POST https://{broker_domain}/sms/message/bulk
{
"templateId": 4,
"campaignId": "summer-promo",
"kind": "MARKETING",
"recipients": [
{
"phone": "+380951234567",
"sendToParentType": "CUSTOMER",
"sendToParentId": 140,
"data": {
"name": "John"
}
},
{
"phone": "+380671111111",
"sendToParentType": "LEAD",
"sendToParentId": 77,
"data": {
"name": "Anna"
}
}
]
}
{
"command": "SmsSendBulk",
"extID": "1",
"data": {
"templateId": 4,
"campaignId": "summer-promo",
"kind": "MARKETING",
"recipients": [
{
"phone": "+380951234567",
"sendToParentType": "CUSTOMER",
"sendToParentId": 140,
"data": {
"name": "John"
}
},
{
"phone": "+380671111111",
"sendToParentType": "LEAD",
"sendToParentId": 77,
"data": {
"name": "Anna"
}
}
]
}
}
const res = await platform.SmsSendBulk({
templateId: 4,
campaignId: "summer-promo",
kind: "MARKETING",
recipients: [
{
phone: "+380951234567",
sendToParentType: "CUSTOMER",
sendToParentId: 140,
data: {
name: "John"
}
},
{
phone: "+380671111111",
sendToParentType: "LEAD",
sendToParentId: 77,
data: {
name: "Anna"
}
}
]
});
Request Data¶
| Field | Type | Required | Description |
|---|---|---|---|
recipients |
array | Yes | Recipients, 1..10000 items: { phone, sendToParentType?, sendToParentId?, data? } |
message |
string | No | Shared text; required unless templateId is set |
templateId |
int | No | Template to render for every recipient |
data |
object | No | Shared placeholder values; per-recipient data overrides them |
profileId |
int | No | Explicit provider profile for the whole campaign |
senderId |
string | No | Sender name or number |
kind |
enum | No | TRANSACTIONAL or MARKETING |
sendAt |
string | No | Start the campaign later |
truncate |
bool | No | Truncate long texts instead of rejecting them |
campaignId |
string | No | Your own campaign id; generated when omitted |
Behavior¶
partscounts segments, not messages: that is the number the operator invoices, and a two-segment text doubles the cost of the campaign.- Messages are queued, not sent inside the request. The queue paces them at the provider rate limit, so a 10 000-recipient campaign cannot flood the account.
campaignIdties the messages together for SmsGetStats and for cancelling what has not left the queue yet.
Response Data¶
{
"campaignId": "summer-promo",
"queued": 2,
"parts": 2,
"failed": [
{ "phone": "380950000000", "error": "Phone 380950000000 is blacklisted: opt-out" }
],
"messages": [
{ "smsId": 814, "phone": "380951234567", "parts": 1 },
{ "smsId": 815, "phone": "380671111111", "parts": 1 }
]
}
Errors¶
| Code | Error | Description |
|---|---|---|
400 |
VALIDATION_ERROR |
Empty recipient list, more than 10 000 items, or neither message nor templateId |
404 |
TEMPLATE_NOT_FOUND |
Template not found in the brand |
409 |
NO_ROUTE_AVAILABLE |
No profile of the brand can send to these destinations |