Skip to content
WaveSenda
Start free
Docs / Messages

Sending and receiving

One endpoint sends every message type. Delivery is asynchronous by default, and every state change comes back to you as a webhook.

Endpoints

POST
/v1/messages/send/
Send any message type
POST
/v1/messages/media/
Upload a file, get a media id
GET
/v1/messages/
List messages
GET
/v1/messages/{id}/
Read one message
POST
/v1/messages/{id}/react/
React with an emoji
POST
/v1/messages/{id}/edit/
Edit sent text
POST
/v1/messages/{id}/revoke/
Delete for everyone
POST
/v1/messages/{id}/mark-read/
Send a blue tick
POST
/v1/messages/presence/
Show typing or recording
GET
/v1/messages/conversations/
List conversations
GET
/v1/messages/conversations/{id}/messages/
Read a thread

The send body

FieldTypeNotes
sessionstringId or handle. Optional if the key is scoped to one number.
tostringA phone number in any format, or a JID. Normalised for you.
typestringDefaults to text.
textstringThe body, for a text message.
captionstringCaption on an image, video or document.
media_iduuidFrom an upload. Send the same file many times without re-uploading.
filefileUpload and send in one multipart request.
latitude / longitudenumberWith optional location_name and location_address.
contactsarrayContact cards, each with a name and a phone.
reply_tostringA WaveSenda or WhatsApp message id to quote.
mentionsstring[]Numbers to @-mention, in a group.
view_oncebooleanMedia that disappears after it is opened.
disable_link_previewbooleanSuppress the URL card.
idempotency_keystringMakes the send safely retryable.
waitbooleanBlock briefly for the WhatsApp id instead of returning on queue.
prioritybooleanJump ahead of campaign traffic. For OTPs and other time-critical sends.

Message types

image, by upload then send
# 1. Upload once
curl -X POST "$WAVESENDA_URL/messages/media/" \
  -H "Authorization: Bearer $WAVESENDA_API_KEY" \
  -F "file=@receipt.png" -F "type=image"
# → { "id": "3c9a...", "type": "image", "url": "https://..." }

# 2. Send it as often as you like
curl -X POST "$WAVESENDA_URL/messages/send/" \
  -H "Authorization: Bearer $WAVESENDA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"session":"support","to":"+237670000000",
       "type":"image","media_id":"3c9a...","caption":"Your receipt"}'
location
{
  "session": "support",
  "to": "+237670000000",
  "type": "location",
  "latitude": 4.0511,
  "longitude": 9.7679,
  "location_name": "Warehouse 3",
  "location_address": "Bonabéri, Douala"
}
a threaded reply with a mention
{
  "session": "support",
  "to": "120363000000000000@g.us",
  "text": "@237670000000 this one is yours.",
  "mentions": ["+237670000000"],
  "reply_to": "wamid.HBgM..."
}

A voice note is its own type rather than plain audio: it renders as a waveform in WhatsApp, and in several markets it is the most common thing people send back to you.

Delivery states

queued and sending are ours; the rest mirror WhatsApp’s own acknowledgement levels. Each transition fires a webhook, so you never have to poll.

StatusMeaning
queuedAccepted and waiting on the session. Not terminal.
sendingHanded to the WhatsApp socket.
sentOne tick. WhatsApp has it.
deliveredTwo ticks. The handset has it.
readBlue ticks — if the recipient shows read receipts at all.
playedA voice note was listened to.
failedTerminal. Read error_code for why.
Read receipts are optional for the recipient
Plenty of people turn them off. Absence of read is not evidence a message was ignored, and building a follow-up rule on it will misfire.

Editing and revoking

WhatsApp allows an edit for about 15 minutes after sending, and a delete-for-everyone for about two days. Both limits are enforced by WhatsApp rather than by us, so a late call fails rather than silently doing nothing.

A revoke is visible
The recipient is left with “This message was deleted”. It removes the content, not the fact that you sent something.

Typing indicators

If an agent takes four seconds to answer, a typing indicator is the difference between a considered reply and a suspiciously instant one.

POST /v1/messages/presence/
{ "session": "support", "to": "+237670000000", "presence": "composing" }

Reaching people who are not there

Not every number has WhatsApp. Check before a bulk send rather than discovering it one failed message at a time — a run of sends to non-existent accounts is itself a signal that counts against the number.

POST /v1/contacts/check/
{ "session": "support", "numbers": ["+237670000000", "+237690000000"] }
Messages · WaveSenda Docs