API Send SMS
This is the high-throughput version of Quick Send SMS, built for applications that send continuously or in large bursts. It sends the same messages, from the same sender IDs, with the same request body — it just accepts your request and confirms it immediately rather than holding the connection open while every recipient is processed.
Reach for it when Quick Send SMS is your bottleneck: high sustained volume, traffic spikes, or a send loop where response time matters more than getting per-recipient detail in the reply.
Because the work happens after the reply, three things change for your integration: the success status, the response body, and when certain failures become visible. Those are the whole of the migration — see Switching from Quick Send SMS.
How it compares to Quick Send SMS
| Quick Send SMS | API Send SMS | |
|---|---|---|
| Endpoint | POST /api/v1/gateway | POST /api/v3/apiClient/sendMessage |
| Messages, senders, pricing | — | Identical |
| Request body | — | Same shape, minus the fields listed under Parameters |
| Success status | 200 | 202 Accepted |
| Response body | data.queueResponse with a per-recipient result | referenceId, acceptedCount, invalidContacts |
Per-recipient MessageId in the response | Yes | No — see Correlating a send with its messages |
| Response envelope | Standard Cellcast envelope (status, data, error, …) | Plain JSON, no envelope |
| Low credit, unregistered sender, opted-out recipient | Can fail the request | Reported after the response — see Failures you will not see in the response |
| Scheduled and delayed sending | Supported | Not supported |
| Best for | Low volume, or when you need per-recipient detail in the reply | Sustained volume and bursts |
Switching from Quick Send SMS
- Change the URL to
https://api.cellcast.com/api/v3/apiClient/sendMessage. - Treat
202as success, not just200. - Read the response as plain JSON — this endpoint does not wrap its success response in the standard Cellcast envelope, so there is no
statusordatafield on a202. Errors are not uniform either: validation and queueing errors use the Cellcast envelope, while authentication errors come back as{ code, message, stack }. See Responses for both shapes. - Stop reading
queueResponse. Get per-message outcomes from your delivery webhook (forwardWebhookUrl), or from Get List Sent Messages.
Parameters
Headers Parameters
| Parameters | Description |
|---|---|
| Authorization | Please add provided Bearer token - linked to your Cellcast account. Check here |
| Idempotency-Key | Optional. A unique string of your own for this send, so a retry after a network timeout is suppressed instead of sending again. Best-effort and time-limited — see Retrying safely. |
Request Body Parameters
The same shape as Quick Send SMS — the body is passed through to the same sending pipeline — with the exceptions called out below the table.
| Name | Example | Description |
|---|---|---|
| sender | #SharedNum# or Sender ID (Custom ID) | Sender ID (Custom ID): Valid characters: A-Z a-z 0-9, space, and Dash (-). A numeric sender ID can be a maximum length of 16 digits. An alpha-numeric Sender ID can be a maximum length of 11 characters. If 'sender' is left blank, the system will default to '#SharedNum#'. *Note: some accounts are billed at 1.3 credits per SMS when sending from a business name or custom number. This is an account-level setting rather than a fixed surcharge — check your own rate card, or ask Cellcast support, before assuming either price. |
| message | SMS Text goes here | String - Required field. To add a new line to the message, use "\r\n" in the message string with only double quotes. |
| contacts | ["+61400000000"] | Required field. A JSON-encoded array of number strings, such as ["+61400000000", "+61400000001"], or a single comma-separated string. Unlike Bulk Send SMS, object entries such as {"number": …, "customString": …} are not supported here. You can include up to 500 valid numbers in a single API call. Supported number formats include : "+61400000000", "61400000000", "0400000000", "400000000" |
| senderType | businessName | Optional, case-sensitive. One of businessName, customNumber, dedicatedNumber, #SharedNum#. For any non-shared sender the value is re-derived from your account's registered sender IDs during processing, so an incorrect value here is corrected rather than honoured. |
| templateId | 6682440ea000000000000000 | Optional. Send a saved template. The template replaces message when it resolves, but message is still required and must be non-blank — this endpoint validates it before the template is looked up, so templateId alone returns 400. See Get SMS Template. |
| longUrl | [{"_id": "68ef60842000000000000000", "shortUrl": "cell", "longUrl": "www.cellcast.com"}] | Attach tracking links to embed short URLs in your message. Reference them as gosite.au/{shortUrl} inside message. To create a link, see Create Tracking Link. To list and get _id/shortUrl/longUrl, see Get Tracking Links. |
| optout | true | Set to true to enable the Custom Opt-Out feature using the optoutId. |
| optoutId | 6682440ea000000000000000 | The unique identifier of the Custom Opt-Out to be used. Check here to get your Custom Opt-Out ID. Leave this empty to use the default custom opt-out set here |
| replyStopToOptOut | false | Set to true to allow users to opt out by replying "STOP". |
| customString | Allow any text in Custom String. eg. ZOHO, Zappier | Allowed characters: letters, numbers, and dashes in Custom String. For the message report, see Get Custom String Message Report |
| campaign_type | promotional | Optional. Campaign classification, as on Quick Send SMS. |
| forwardReplyTo | [email protected] | Optional. Email address to forward replies to. |
| forwardWebhookUrl | https://example.com/hooks/dlr | Optional but recommended. Your endpoint for delivery receipts and replies, pushed as they happen. You can also query message reports afterwards — see Correlating a send with its messages. |
Not supported on this endpoint
scheduleAt and delay are ignored. A request carrying either is sent immediately — you will not get an error, and the message will not wait. For scheduled or delayed sending use Quick Send SMS or Bulk Send SMS.
countryCode is also ignored; recipient numbers are validated as Australian numbers.
Custom fields work exactly as they do for Quick Send SMS — see Using Custom Fields in the Message Body.
Failures you do get in the response
These are checked while your request is still open, and come back as a 400 with nothing queued — exactly as they would on Quick Send SMS:
| Check | Error message |
|---|---|
message present and not blank | Message is required |
message is at most 10 SMS parts | Limit exceeded: Only 10 message parts are allowed. |
contacts present and not empty | Contacts are required |
| At least one number is valid | No valid contacts |
| At most 500 valid numbers | Max number of contacts allowed in QuickSMS: 500 |
Two notes on contacts:
- Duplicates are collapsed. The same number listed five times is accepted once and billed once.
acceptedCountreflects the deduplicated total. - Invalid numbers do not fail the request. As long as one number is valid, you get a
202; the rejected ones come back ininvalidContactswith a reason. The 500 limit applies to the valid numbers that remain after invalid ones and duplicates are removed.
Correlating a send with its messages
referenceId identifies your request, not the messages it produces — it is not stamped onto the individual messages and cannot be looked up. There is no endpoint that takes a referenceId.
To tie messages back to a request, use customString: set your own value per send and read it back through Get Custom String Message Report. Set forwardWebhookUrl as well, so delivery receipts reach you as they happen.
Failures you will not see in the response
These are checked after your request has already been answered, so a 202 does not mean they passed. The request is accepted and the affected messages are then not sent:
| Condition | What happens |
|---|---|
| Insufficient credit | Messages are not sent. Quick Send SMS fails the call with 422 instead — use it if you need that. |
| Sender ID not registered to your account | The whole request is dropped, every recipient, not just some. Quick Send SMS rejects this with a 400. |
| Recipient on your opt-out list, or a blocked number | The message is never sent — opt-out is always enforced — but the credit is still charged and not refunded. This matches how bulk campaigns bill; Quick Send SMS does not charge for these. |
| Content caught by fraud / offensive-word filtering | The campaign is held for manual approval instead of being sent. |
templateId cannot be resolved | The whole request is dropped. The template is looked up after the response, so a bad id is not reported to you. |
WARNING
Because none of the above reaches you in the response, confirm a new integration end-to-end on a small send before switching production traffic over — in particular that your sender is registered and approved for your account.
Retrying safely
Without an Idempotency-Key, a retry is treated as a brand new send and the messages go out again — the same behaviour as Quick Send SMS.
Send an Idempotency-Key header with a unique value per send (an order ID, a UUID). The same key always produces the same referenceId, and processing is designed to suppress a duplicate carrying a referenceId it has already completed — so you can retry a request that timed out without knowing whether the original arrived. Keys are scoped to your account, so your key can never collide with another customer's.
Note that the retry is accepted and queued again; the suppression happens during processing, not at the time of your call. You will get a 202 either way.
WARNING
Duplicate suppression lasts roughly one hour after the original send finishes processing. A retry with the same key after that window is treated as a new send. It is best-effort, not a delivery guarantee — do not rely on it as your only safeguard against duplicate sends.
Code Samples
Post https://api.cellcast.com/api/v3/apiClient/sendMessage
curl --location 'https://api.cellcast.com/api/v3/apiClient/sendMessage' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {{API_KEY}}' \
--header 'Idempotency-Key: order-4417-attempt-1' \
--data '{
"message": "Hi {{[First Name]}}, Test Message Visit gosite.au/cell ",
"contacts": [
"+61400000000",
"+61400000001"
],
"sender": "+61400000001",
"longUrl": [
{
"_id": "68ef60842000000000000000",
"longUrl": "www.cellcast.com",
"shortUrl": "cell"
}
]
}'const axios = require("axios");
const sendSMS = async () => {
try {
const response = await axios.post(
"https://api.cellcast.com/api/v3/apiClient/sendMessage",
{
message: "Hi {{[First Name]}}, Test Message Visit gosite.au/cell",
contacts: ["+61400000000", "+61400000001"],
sender: "+61400000001",
longUrl: [
{
_id: "68ef60842000000000000000",
longUrl: "www.cellcast.com",
shortUrl: "cell"
}
]
},
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: "Bearer {{API_KEY}}",
"Idempotency-Key": "order-4417-attempt-1",
},
}
);
// 202 Accepted — plain JSON, no envelope.
console.log(response.data.referenceId, response.data.acceptedCount);
} catch (error) {
console.error(error.response.data);
}
};Replace with the actual API key that you own.
Responses
Response Body
| Name | Example | Description |
|---|---|---|
| referenceId | 6544b052-55da-48c5-a0b7-23be013b4006 | Identifier for this request. Used for duplicate suppression on retries (see Retrying safely) and useful in your own logs when contacting support. See the note below on correlating it with individual messages. |
| acceptedCount | 2 | How many recipients were accepted for sending, after invalid numbers and duplicates were removed. |
| invalidContacts | [] | Numbers that were rejected, each with the reason. Empty when every number was valid. |
| invalidContacts[].Contact | 123 | The number exactly as you supplied it. |
| invalidContacts[].Error | Number is less than 9 digit | Human-readable reason. |
| invalidContacts[].ErrorType | less_nine_digit | Machine-readable reason code. |
202 Accepted
Two numbers accepted, one rejected. Note there is no envelope around this body.
{
"referenceId": "6544b052-55da-48c5-a0b7-23be013b4006",
"acceptedCount": 2,
"invalidContacts": [
{
"Contact": "123",
"Error": "Number is less than 9 digit",
"ErrorType": "less_nine_digit"
}
]
}202 Accepted (with Idempotency-Key)
When you send an Idempotency-Key, the referenceId is derived from it, so retrying with the same key returns the same referenceId.
{
"referenceId": "a00f454bc35244826b30f8910feb7611a45c90527fa42dbbb9d097322a97c7cb",
"acceptedCount": 1,
"invalidContacts": []
}400 Bad Request (message missing)
{
"app_type": "web",
"app_version": "1.0",
"maintainence": 0,
"new_version": 0,
"force_update": 0,
"invalid_token": 0,
"refresh_token": "",
"show_message": 0,
"is_enc": false,
"status": false,
"message_type": "toast",
"message": "Message is required",
"data": {},
"error": {
"message": "Message is required"
}
}400 Bad Request (no valid contacts)
Every supplied number was invalid. When at least one is valid you receive a 202 instead, with the rest listed in invalidContacts.
{
"app_type": "web",
"app_version": "1.0",
"maintainence": 0,
"new_version": 0,
"force_update": 0,
"invalid_token": 0,
"refresh_token": "",
"show_message": 0,
"is_enc": false,
"status": false,
"message_type": "toast",
"message": "No valid contacts",
"data": {},
"error": {
"contacts": "No valid contacts"
}
}400 Bad Request (too many contacts)
{
"app_type": "web",
"app_version": "1.0",
"maintainence": 0,
"new_version": 0,
"force_update": 0,
"invalid_token": 0,
"refresh_token": "",
"show_message": 0,
"is_enc": false,
"status": false,
"message_type": "toast",
"message": "Max number of contacts allowed in QuickSMS: 500",
"data": {},
"error": {
"contacts": "Max number of contacts allowed in QuickSMS: 500"
}
}401 Unauthorized
{
"code": 401,
"message": "Token expired",
"stack": "APIError: Token expired ...."
}500 Internal Server Error
The send could not be queued. Nothing was accepted, so it is safe to retry — reuse the same Idempotency-Key if you sent one.
{
"app_type": "web",
"app_version": "1.0",
"maintainence": 0,
"new_version": 0,
"force_update": 0,
"invalid_token": 0,
"refresh_token": "",
"show_message": 0,
"is_enc": false,
"status": false,
"message_type": "toast",
"message": "SEND_INGEST_SQS_URL is not configured",
"data": {},
"error": {
"serverError": "SEND_INGEST_SQS_URL is not configured"
}
}