Sending Messages
Use POST /api/core/messages/send to queue outbound messages through the Red Cloud message pipeline.
This route accepts an outbound request into the Red Cloud queue-first messaging flow. It does not synchronously guarantee carrier acceptance or handset delivery.
Canonical example values
access_token:rc_token_examplenumberId:num_abc123messageId:msg_abc123externalReference:order_789phone:+15551234567
Required headers
Authorization: Bearer rc_token_exampleIdempotency-Key: idem_order_789
Idempotency-Key is required.
Why Idempotency-Key matters
Send requests are the highest-risk place to create accidental duplicates. Always generate and persist an idempotency key that is stable for the business operation being performed.
Required clarifications
sendFrom
sendFrom must be a Red Cloud numberId.
It is:
- a number identifier such as
num_abc123 - not an E.164 phone number
If you pass a phone number instead of numberId, treat that as an integration bug, not as a routing issue.
to[]
Every recipient in to[] must be E.164 formatted.
Example:
+15551234567
If you send multiple recipients, evaluate both results[] and errors[]. The route may partially accept the request.
Before you send
Confirm:
- you already have a valid bearer token
- you already verified
/api/core/whoami - the
numberIdcame from/api/core/numbers - the chosen number is enabled for the message type you intend to send
Minimal send example
Request
curl -X POST "$BASE_URL/api/core/messages/send" \
-H "Authorization: Bearer rc_token_example" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_order_789" \
-d '{
"sendFrom": "num_abc123",
"to": ["+15551234567"],
"messageType": "SMS",
"text": "Hello from Red Cloud",
"externalReference": "order_789"
}'
Success response
{
"results": [
{
"to": "+15551234567",
"messageId": "msg_abc123",
"status": "QUEUED"
}
],
"errors": []
}
What success means here
QUEUED means the request was accepted by Red Cloud. It does not mean:
- a provider accepted the message
- the recipient handset received the message
- final delivery is complete
Use webhook events for lifecycle truth after the request is queued.
MMS example
Request
curl -X POST "$BASE_URL/api/core/messages/send" \
-H "Authorization: Bearer rc_token_example" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_order_789_mms" \
-d '{
"sendFrom": "num_abc123",
"to": ["+15551234567"],
"messageType": "MMS",
"text": "Catalog preview",
"media": [
{
"storageKey": "storage_abc123",
"mimeType": "image/jpeg",
"byteLength": 182340,
"sourceUrl": "https://example.com/files/catalog.jpg",
"fileName": "catalog.jpg"
}
],
"externalReference": "order_789"
}'
MMS note
The media array must be present for MMS. An SMS send must not include media.
Response interpretation
The route returns:
results[]for accepted recipientserrors[]for rejected recipientsmessageIdfor tracking accepted recipients through webhook lifecycle updates
Partial success example
{
"results": [
{
"to": "+15551234567",
"messageId": "msg_abc123",
"status": "QUEUED"
}
],
"errors": [
{
"to": "invalid",
"error": {
"code": "INVALID_RECIPIENT",
"message": "Recipient must be a valid E.164 phone number",
"details": {}
}
}
]
}
Request error example
{
"error": {
"code": "INVALID_REQUEST",
"message": "Idempotency-Key header is required",
"details": {}
},
"requestId": "req_send_001"
}
Actual implementation rules
- at least one recipient is required
SMScannot include mediaMMSmust include media- at least one of
textormediais required
Recommended client handling
For each accepted recipient:
- persist
messageId - persist
externalReferenceif you supplied one - correlate future webhook events by
messageId - do not mark the message delivered from the synchronous response alone
For each rejected recipient:
- inspect the per-recipient error
- correct the bad recipient or payload issue
- retry only with a deliberate idempotency strategy
Idempotency behavior
- same
Idempotency-Key+ same payload:- returns the stored
202response again
- returns the stored
- same
Idempotency-Key+ different payload:- returns
409 CONFLICT
- returns
Use idempotency to safely retry client-side sends without duplicating downstream messages.
Common send mistakes
- using E.164 in
sendFrom - omitting
Idempotency-Key - assuming
QUEUEDmeans delivered - not storing
messageId - retrying with a changed payload under the same idempotency key
Next step after send
Once sends are accepted, move to: