Base URL: https://api.notideus.io. WhatsApp sends go through dashboard-managed templates — the API references them by template_id (Templates). Failures use the standard error envelope: Errors.
POST /v1/whatsapp/messages
Scope: whatsapp:send — dual-auth: also accepts a session token.
Enqueues one template message. One message row is created per recipient, so to with 3 phones yields 3 rows.
Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
template_id | uuid | yes | Dashboard-managed template |
to | string | yes | E.164 ^\+[1-9]\d{7,14}$; separators stripped; 1–50 numbers = one row each |
parameters | mapstringstring | if the template declares slots | Keys are scope:position (e.g. body:1, plus url:1 when the template has a dynamic URL button); every declared slot required, unknown keys rejected |
idempotency_key | string | no | Replay returns 200 + original |
Request example
{
"template_id": "9f2c7a1e-0b1c-4d3e-8a5f-2e6b9c4d7a01",
"to": ["+33612345678"],
"parameters": {
"body:1": "Jane"
},
"idempotency_key": "order-12345-whatsapp"
}
Response
201 Created — one row per recipient. A real response:
{
"data": [
{
"id": "01a0a11d-a584-72f2-b86d-dad8e76b2c97",
"to": "+33612345678",
"status": "queued"
}
]
}
Row shape: id (uuid, the stored message), to (the recipient this row was created for), status (one of queued, sent, delivered, read, failed).
Errors
- 400
invalid_phone— a number intois not valid E.164. - 400
invalid_params— a declared template slot is missing, or an unknown parameter key was sent. - 400
template_disabled/template_unsupported— the template exists but is not sendable. - 402
quota_exceeded— send allowance reached; see Quotas and limits. - 403
insufficient_scope— the key lackswhatsapp:send. - 404
not_found— unknowntemplate_id(visible whole-call on single send, per-item in batch).
See Errors for the full table.
POST /v1/whatsapp/messages/batch
Scope: whatsapp:send — API key only (no session auth).
Enqueues up to 100 items in one call. The body is a messages array; each item has the same shape as a single send, including its own idempotency_key.
Request example
{
"messages": [
{
"template_id": "9f2c7a1e-0b1c-4d3e-8a5f-2e6b9c4d7a01",
"to": ["+33612345678"],
"parameters": { "body:1": "Jane" }
},
{
"template_id": "9f2c7a1e-0b1c-4d3e-8a5f-2e6b9c4d7a01",
"to": ["+33655550100"],
"parameters": { "body:1": "John" }
}
]
}
Response
200 OK — one indexed result per item, same envelope as the email batch. A real response for a batch whose first item had a bad phone:
{
"data": [
{
"index": 0,
"error": {
"code": "invalid_phone",
"message": "to[0]: invalid_phone: +abc is not a valid E.164 phone number (e.g. +33612345678)"
}
}
]
}
Decode: index is the item's position in the messages array, and to[0] marks which entry in that item's to array was rejected — the doubled invalid_phone label in the message is verbatim from the API. Partial failures never abort the batch; only failed items need retrying, with the same per-item idempotency_keys.
Whole-call failures
- 400
batch_too_large— more than 100 items. - 402
quota_exceeded— the whole batch is checked against your quota up front; idempotent replays are exempt.
Statuses move queued → sent → delivered → read (or failed) as Meta reports back. Status visibility is dashboard-only today — see Tracking and events. Walkthrough: Sending WhatsApp messages.