Skip to content

Send a message

POST /messages/whatsapp — scope WHATSAPP_SEND.

A message is a template, free text or media, exactly one of the three.

Field Type Required Notes
to string yes the recipient, international form: +919876543210
from string no which of your numbers to send from. Omitted: your default number, or your only one
template object one of three { name, language, variables } — the only form deliverable outside the window
text string one of three 1–4096 characters. Inside the window only
media object one of three see Media
idempotencyKey string no 8–128 characters. A repeat returns the first result instead of sending again
metadata object no your own JSON, stored and returned on every read of this message

The only form Meta allows outside the 24-hour window.

{
"to": "+919876543210",
"from": "+919812345678",
"template": {
"name": "order_shipped",
"language": "en_US",
"variables": { "name": "Ravi", "order_id": "BLR-4471" }
},
"idempotencyKey": "order-4471-shipped"
}

variables is an object, not a list, because WhatsApp templates can be named as well as numbered:

{ "name": "Ravi", "order_id": "BLR-4471" } // named: {{name}}, {{order_id}}
{ "1": "Ravi", "2": "BLR-4471" } // numbered: {{1}}, {{2}}

language defaults to en_US. Every variable the template declares must be present and non-empty, or the send is refused with 400 MISSING_VARIABLES — GET /templates tells you which ones a template needs.

Inside an open window only.

{ "to": "+919876543210", "text": "Your order is out for delivery." }

metadata is yours: up to 50 keys and 8 KB once serialized, keys up to 64 characters. Use it to carry your own order or ticket id so a webhook or a log read can be matched back to your records without a lookup table.

We never read inside it, so never put a secret there.

201 Created, or 200 OK when an idempotencyKey replayed:

{
"data": {
"id": "cmu102948182741",
"channel": "WHATSAPP",
"status": "SENT",
"to": "+919876543210",
"from": "+919812345678",
"providerMessageId": "wamid.HBgM109…",
"metadata": { "orderId": "A-1043" },
"error": null,
"createdAt": "2026-09-27T10:00:00.000Z"
},
"meta": { "replayed": false }
}

status is SENT once Meta accepted it, FAILED if Meta refused — with the reason in error. Delivery is not confirmed yet: DELIVERED and READ arrive later, as webhooks or on a later read.

idempotencyKey is scoped to your workspace. Re-sending the same key returns the original message with 200 and meta.replayed: true, and never calls Meta twice — which is what makes a retry after a timeout safe.

Key it on the event that caused the send — order-4471-shipped — not on a fresh UUID. A random key per attempt is not idempotent.

Failure HTTP Code
free text or media with no open window 409 WINDOW_CLOSED
no template with that name and language 404 TEMPLATE_NOT_FOUND
template not approved by Meta 409 TEMPLATE_NOT_APPROVED
a template variable is missing or blank 400 MISSING_VARIABLES
from is not one of your numbers 404 NUMBER_NOT_FOUND
from has not finished registering with Meta 409 NUMBER_NOT_REGISTERED
you have no number that can send yet 409 CHANNEL_NOT_CONFIGURED

POST /messages/whatsapp/preview — scope WHATSAPP_SEND. Fills a stored template with the values you pass and returns the text Meta would render, without sending anything. Useful for showing an operator what is about to go out.

Terminal window
curl -X POST "https://vaarto.com/api/v1/messages/whatsapp/preview" \
-H "Authorization: Bearer $VAARTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateName": "order_shipped",
"language": "en_US",
"variables": {
"1": "Rahul",
"2": "ORD-9921"
}
}'

variables accepts an object (named or numbered keys) or a plain array in placeholder order.

{
"data": {
"templateName": "order_shipped",
"language": "en_US",
"category": "UTILITY",
"status": "APPROVED",
"header": "Order Status Update",
"body": "Hi Rahul, your order ORD-9921 is confirmed.",
"footer": "Thank you for shopping with us!",
"buttons": ["Track Order"],
"fullText": "Order Status Update\n\nHi Rahul, your order ORD-9921 is confirmed.\n\nThank you for shopping with us!"
}
}