Sending messages
One request sends one message to one number: POST /v1/messages.
json
{
"to": "+5521998120937",
"channel": "whatsapp",
"template": "welcome_bonus",
"language": "pt",
"variables": { "name": "Ana", "promo_code": "BONUS10" },
"reference": "crm-42"
}| Field | |
|---|---|
to | The number in E.164: +, country code, number. No spaces, dashes, 00 or a missing +. |
channel | whatsapp (default) or sms. |
template | The name of a template of the channel, as GET /v1/templates lists it. |
language | One of the languages of the template, exactly as written there (pt, pt_BR). |
variables | Values of the template variables by key, strings up to 1000 characters. |
reference | Optional: your own id of the message, up to 64 characters. It comes back in the message and its webhooks, and GET /v1/messages?reference= finds it. It does not have to be unique. |
text | SMS only, instead of a template: an own text. See SMS. |
WhatsApp messages go out from ACE SEND numbers: there is no sender to choose. SMS go out from the sender name of your account — see SMS.
Templates and variables
Templates are created in the console, per channel, in one or more languages. A text refers to variables as {{name}}. GET /v1/templates returns, for each template you can send, its texts, variables, an example value and the fallback of each variable per language.
For every variable of the text:
- the value you send in
variablesis used; - a missing or blank value takes the fallback of the template in that language;
- no value and no fallback — the request is refused:
422 validation_failedwith the fieldvariables.<key>and the coderequired.
Keys that the template does not use are ignored. The final text is returned in text.
What happens after 202
The answer means the message passed every check, its price is fixed and reserved, and it is queued. It has the status queued. Then:
- ACE SEND hands it to the provider (
submitted); - the provider reports
sent,deliveredand, for WhatsApp,read— or a failure; - each of these comes to your webhook address and into
GET /v1/messages/{id}.
Checks and refusals
The request is checked in this order; the first failed check answers. A refused request creates no message and costs nothing.
| Check | Refusal |
|---|---|
| The account is active | 403 account_suspended |
| The body is valid | 422 validation_failed |
| The number is a valid number of a known country | 422 invalid_number |
| The template exists in the channel, the language is one of its languages | 404 template_not_found, 422 validation_failed (language) |
| WhatsApp: the template can be sent through WhatsApp now | 409 template_unavailable |
| Every variable has a value or a fallback | 422 validation_failed (variables.<key>) |
| SMS: an own text is allowed, the text fits the segment limit | 403 free_text_not_allowed, 422 message_too_long |
| The number is not on a stop list of the account | 422 recipient_stopped |
| The channel delivers to the country, there is a price (SMS: and a sender name) | 422 destination_unavailable |
| The daily limit of the account is not reached | 429 daily_limit_reached |
| The available balance covers the price | 409 insufficient_funds |
Each code is described in Errors.
Sending in bulk
The API sends one message per request, up to 50 requests a second per key (200 a second per account). For a mailing to a large list, a campaign in the console is usually the better tool: it schedules, paces, pauses and reports for you. If you send from your own system, spread the requests, retry 429 after Retry-After and give each message an Idempotency-Key.