Messages API

Send text, media and templates; read the delivery timeline. 8 ops: send_message, send_bulk_messages, get_message, list_messages, cancel_message, retry_message, mark_message_read, get_media_url.

Send text, media and templates; read the delivery timeline.

Send a WhatsApp message · POST /api/v1/send_message · scope messages:write

Sends one WhatsApp message from a connected number (or the sandbox): text, template, image/audio/video/document/sticker, location, contacts, interactive (buttons, lists, CTA URL, Flows) or a reaction, optionally as a reply. Safeguards run first, in order: account and number eligibility, opt-out and consent, the 24-hour window (outside it only approved templates), template approval, rate limits (the send waits instead of failing), the plan's allowance and budget cap, and duplicate detection; a refusal says which (`safeguard`) and whose call it is (`controlled_by`). Returns the message: status submitted once Meta accepted it, then sent → delivered → read arrive by webhook (get_message shows the timeline). Use idempotency_key to make retries safe.

curl -X POST https://getwabi.dev/api/v1/send_message \  -H "Authorization: Bearer wabi_…" \  -H "Content-Type: application/json" \  -d '{"type":"text","phone_number_id":"phone_number_id","to":"15550100001"}'

Send to many recipients · POST /api/v1/send_bulk_messages · scope messages:write

Queues the same message (usually an approved template, with per-recipient variables) to up to 500 customers from one number, now or at scheduled_at. Every recipient passes the same safeguards as send_message; refused ones are listed with the reason and nothing is stored for them. Accepted messages go out through the queue at the number's rate limit. Live numbers need a plan with bulk sends (Growth and up); the sandbox is free. Returns the queued message ids and the refusals.

curl -X POST https://getwabi.dev/api/v1/send_bulk_messages \  -H "Authorization: Bearer wabi_…" \  -H "Content-Type: application/json" \  -d '{"type":"text","phone_number_id":"phone_number_id","recipients":[{"to":"15550100001"}]}'

Get a message · POST /api/v1/get_message · scope messages:read or GET …?input=

One message with its full status timeline (queued, submitted, sent, delivered, read, or failed with Meta's error code and whose problem it is), Meta's pricing category, attempts and retries.

curl -X POST https://getwabi.dev/api/v1/get_message \  -H "Authorization: Bearer wabi_…" \  -H "Content-Type: application/json" \  -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'

List messages · POST /api/v1/list_messages · scope messages:read or GET …?input=

Messages, newest first, filtered by conversation, contact, number, direction or status; dead_letter: true lists sends that exhausted their retries (retry_message re-queues them).

curl -X POST https://getwabi.dev/api/v1/list_messages \  -H "Authorization: Bearer wabi_…"

Cancel a message · POST /api/v1/cancel_message · scope messages:write

Cancels an outbound message that has not gone to WhatsApp yet (scheduled, queued, rate limited or held). A message Meta already accepted cannot be recalled.

curl -X POST https://getwabi.dev/api/v1/cancel_message \  -H "Authorization: Bearer wabi_…" \  -H "Content-Type: application/json" \  -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'

Retry a message · POST /api/v1/retry_message · scope messages:write

Re-queues an outbound message that never reached WhatsApp (dead-lettered after its retries, refused by Meta, or expired) for a fresh set of attempts, through the same safeguards at send time. Fix the cause first (a template, a token, the window).

curl -X POST https://getwabi.dev/api/v1/retry_message \  -H "Authorization: Bearer wabi_…" \  -H "Content-Type: application/json" \  -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'

Mark a message read · POST /api/v1/mark_message_read · scope messages:write

Marks an inbound message as read on the customer's phone (blue ticks), optionally showing a typing indicator while a reply is written (WhatsApp shows it up to 25 seconds or until you reply).

curl -X POST https://getwabi.dev/api/v1/mark_message_read \  -H "Authorization: Bearer wabi_…" \  -H "Content-Type: application/json" \  -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'

Get a media link · POST /api/v1/get_media_url · scope messages:read or GET …?input=

A link (valid 10 minutes) to the image, voice note, video, document or sticker a customer sent; the file is kept in our storage, so it outlives Meta's own short-lived link. Returns the URL, MIME type and size.

curl -X POST https://getwabi.dev/api/v1/get_media_url \  -H "Authorization: Bearer wabi_…" \  -H "Content-Type: application/json" \  -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'