Skip to content

Send individual (personalized bulk)

Copy page

Send a personalized bulk of SMS in a single request: every recipient gets its own message text and its own sender ID. This is the difference from /sms/send, which fans one message out to many recipients.

POST  /api/v1/sms/send-individual  · Permission: sms.send

Each accepted recipient is queued, billed and tracked exactly like a normal send, and gets its own message_id in the response — so you can correlate delivery by message_id or by phone number via GET /sms/messages. A blocked_country recipient is returned in results[] without a message_id and is not queued. Missing local pricing does not reject a valid live recipient: it is accepted and queued with a provisional local cost.

Terminal window
curl -X POST https://restlink23telecom.com/api/v1/sms/send-individual \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: send-20260627-batch-001" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+14155551234",
"message": "Hi John, your code is 847291",
"sender_id": "MyApp"
},
{
"to": "+447911123456",
"message": "Hi Anna, your code is 113355",
"sender_id": "MyApp"
}
]
}'
FieldTypeRequiredDescription
Idempotency-Key headerstringYesStable key for this logical batch. Reuse it after a timeout or 5xx. Completed responses are retained for 24 hours and replayed byte-for-byte with Idempotent-Replayed: true; a replay is not a new send. A key used with a different body returns 409 IDEMPOTENCY_KEY_REUSED
messagesobject[]Yes1 to 1000 messages
messages[].tostringYesRecipient phone number in E.164 format (+ optional, 6-15 digits)
messages[].messagestringYesMessage text for this recipient — encoding is detected automatically; max 10 SMS segments
messages[].sender_idstringYesSender ID for this recipient: 3-11 ASCII alphanumeric characters, first character cannot be a number

The API trims leading and trailing whitespace from each to, message and sender_id before validation. Values that are blank after trimming, malformed phone numbers, over-10-segment messages, and invalid senders return 400.

The response shape is identical to /sms/send: each accepted recipient appears in both messages and results with its own message_id. When a completed request is retried with the same Idempotency-Key, the API returns the stored response, does not consume batch tokens again, and includes Idempotent-Replayed: true.

200 OK
{
"status": true,
"messages": [
{"dnis": "+14155551234", "message_id": "api_42_1743667200123456789_a3f8b2c1d9e45f67", "segment_num": 1},
{"dnis": "+447911123456", "message_id": "api_42_1743667200123456789_b7c4e8f1a2d3690b", "segment_num": 1}
],
"results": [
{"dnis": "+14155551234", "message_id": "api_42_1743667200123456789_a3f8b2c1d9e45f67", "segments": 1, "status": "accepted"},
{"dnis": "+447911123456", "message_id": "api_42_1743667200123456789_b7c4e8f1a2d3690b", "segments": 1, "status": "accepted"}
],
"summary": {
"total_recipients": 2,
"total_segments": 2,
"total_cost": 0.02,
"encoding": "GSM-7",
"accepted_count": 2,
"blocked_count": 0,
"unpriced_count": 0,
"queue_error_count": 0,
"config_error_count": 0,
"db_error_count": 0
}
}
FieldDescription
messagesAccepted messages only (one entry per recipient)
resultsAll recipients with their individual status
summaryTotals: accepted, blocked, errors, cost and batch encoding. encoding is GSM-7 or UCS-2 when at least one recipient is accepted, and can be empty when no recipient is accepted
results[].statusMeaning
acceptedQueued for delivery
blocked_countryRecipient’s country is in your blocked list
unpricedLegacy/sandbox non-acceptance status retained for compatibility. Current live pricing misses are accepted and queued with a provisional local cost
queue_errorSandbox simulator storage failure (sk_test_ only); live Redis enqueue failures stay accepted and are recovered from the durable outbox
config_errorSMS sending not configured on the account
db_errorThe recipient’s outbox row could not be written
HTTPCodeDescription
400INVALID_BODYCannot parse request body
400INVALID_MESSAGESMissing or empty messages array
400IDEMPOTENCY_KEY_REQUIRED / INVALID_IDEMPOTENCY_KEYMissing or invalid Idempotency-Key header
400TOO_MANY_MESSAGESOver 1000 messages
400INVALID_TOA messages[i].to is missing or not E.164-style
400INVALID_MESSAGEA messages[i].message is missing or exceeds 10 SMS segments
400INVALID_SENDERA messages[i].sender_id is missing or not 3-11 ASCII alphanumeric characters
403NO_SMS_ACCESSSMS not enabled on your account
403CONFIG_ERRORSMS credentials incomplete — contact support
403WORKSPACE_NOT_AVAILABLETarget workspace was deleted — use a live workspace
409IDEMPOTENCY_KEY_REUSEDSame key was already used with a different request body
409IDEMPOTENCY_REQUEST_IN_PROGRESSSame key is still processing; retry later with the same key
429RATE_LIMIT_EXCEEDEDThe batch exceeds your per-second message budget — see the note below
500DB_ERRORCould not queue (atomic rollback) — retry the whole request
503SANDBOX_UNAVAILABLE / RATE_LIMIT_UNAVAILABLE / QUEUE_UNAVAILABLESandbox simulator not enabled (sk_test_), rate limiter unavailable, or SMS queue unavailable — retry shortly with the same Idempotency-Key

The send response confirms acceptance, not delivery. Because every recipient gets a distinct message_id, you can track each one precisely:

  • Webhooks (recommended): receive a delivery report per recipient the moment the carrier reports it.
  • Per message: call GET /sms/status/:message_id for any message_id returned above.
  • By phone: call GET /sms/messages with phone=, from= and to= to pull every message to a number over a date range and match results by recipient.