Notideus
Tutorials

Sending WhatsApp messages

Send template WhatsApp messages with parameters and E.164 phones.

Prerequisites

  • A WhatsApp template synced in the dashboard. Template management is dashboard-only — the API references templates by template_id.
  • An API key with the whatsapp:send scope.

Payload

POST /v1/whatsapp/messages takes one item; one message row is created per recipient:

FieldTypeRequiredNotes
template_iduuidyesDashboard-managed template
tostringyesE.164 ^\+[1-9]\d{7,14}$; separators stripped; 1–50 numbers = one row each
parametersmapstringstringif the template declares slotsKeys 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_keystringnoReplay returns 200 + original

Request:

const messages = await notideus.whatsapp.messages.send({
  template_id: '9f2c7a1e-0b1c-4d3e-8a5f-2e6b9c4d7a01',
  to: ['+33612345678'],
  parameters: {
    'body:1': 'Jane'
  },
  idempotency_key: 'order-12345-whatsapp'
});
// one entry per recipient: [{ id, to, status }]

A real response (201 Created) — one row per recipient:

{
  "data": [
    {
      "id": "01a0a11d-a584-72f2-b86d-dad8e76b2c97",
      "to": "+33612345678",
      "status": "queued"
    }
  ]
}

With three recipients, data holds three such rows.

Parameters

Templates declare slots and you fill them by scope:position keys — body:1, header:1, and so on, plus url:1 when the template has a dynamic URL button:

  • Every declared slot must be present and non-empty.
  • Unknown keys are rejected.

Violations come back as 400 invalid_params, e.g. invalid parameters: missing body:2 or invalid parameters: unknown foo:1.

Phone numbers

Numbers must be E.164 — ^\+[1-9]\d{7,14}$. Cosmetic separators (spaces, dashes, dots, parentheses) are stripped before validation, so +33 6 12 34 56 78 is accepted as +33612345678. Each item takes 1–50 recipients, and every recipient gets its own row and status — 50 phones in, 50 rows out.

Batches and failures

POST /v1/whatsapp/messages/batch mirrors the email batch: up to 100 items in a messages array, one indexed result per item. Anything unparseable fails just that item — this is 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.)

With the SDK: await notideus.whatsapp.messages.sendBatch([...]) takes the same item shape and resolves with the same indexed per-item results — batch item failures are returned, not thrown, exactly like email batches.

In PHP: $notideus->whatsapp()->messages()->sendBatch([...]) takes the same item shape and returns the same indexed per-item results — batch item failures are returned, not thrown, exactly like email batches.

Statuses

Each row moves queued → sent → delivered → read (or failed) as Meta reports back via webhook.

WhatsApp statuses are dashboard-only today — the API accepts sends but exposes no per-message status polling or outbound webhooks. Watch progress in the dashboard. See Tracking and events.
Copyright © 2026