WhatsApp Gateway
Guides

Send messages

Send text, polls, locations, and contact cards through one REST endpoint, with idempotent retries and per-session rate limits.

Sending is one POST to a session's messages endpoint. You need a session that is already connected (its status is working). Every request body has two required fields:

  • type — what kind of message to send.
  • to — the recipient's JID. A JID is WhatsApp's address for a chat: a phone JID like 628123456789@s.whatsapp.net for a one-to-one chat, or a group JID for a group.

The other fields depend on type.

curl -X POST https://your-gateway/api/v1/sessions/{id}/messages \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"text","to":"628123456789@s.whatsapp.net","text":"Hello"}'

The reply is a SendResult: the WhatsApp message id (messageId), the delivery status, and a timestamp. The full request and response shapes are in send a message.

Message types

typeRequired fieldsOptional fields
texttextmentions (a list of JIDs to tag), replyTo (a message id to quote)
pollname, optionsselectableCount — how many options one voter may pick. Votes come back as poll.vote events.
locationlatitude, longitudename — a label for the pin
contactcontact
image, video, audio, document, stickermedia.datamedia.mimetype, media.caption (image/video/document), media.filename (document), replyTo, mentions

A contact is a card. Give it { name, phone }, or supply a raw vcard string instead.

Media

Media is sent inline: put the file's bytes, base64-encoded, in media.data. The gateway decodes them, uploads them to WhatsApp, and sends the message — no URL fetching, so the request is self-contained. A data: URI (e.g. what a browser's FileReader.readAsDataURL produces) is accepted too; its MIME type is used when you don't set media.mimetype (otherwise the type is detected from the bytes).

The decoded file must be 16 MiB or smaller. mimetype is optional but recommended; filename shows on documents; caption shows under image, video, and document messages.

{
  "type": "image",
  "to": "628123456789@s.whatsapp.net",
  "media": {
    "data": "iVBORw0KGgoAAAANSUhEUgAA...",
    "mimetype": "image/png",
    "caption": "Here you go!"
  }
}

The same shape sends video, audio, document, and sticker — change type (and add media.filename for a document).

The uploaded bytes are not retained: the gateway holds them only long enough to deliver the message (for an async send, until the queued row is drained), then drops them. The stored message keeps the caption and media metadata, never the file content.

A poll request looks like this:

{
  "type": "poll",
  "to": "628123456789@s.whatsapp.net",
  "name": "Lunch?",
  "options": ["Pizza", "Sushi", "Salad"],
  "selectableCount": 1
}

Change or reference a sent message

Once a message exists, these endpoints act on it by its message id:

ActionEndpoint
Edit the textedit a message
Delete for everyonerevoke a message
React with an emojiadd / remove a reaction
Forward it to another chatforward a message
Vote on a pollvote

Sync vs. async

By default the send is synchronous: the call blocks until WhatsApp acknowledges the message, then returns 200 with the messageId and status.

Add ?async=true to enqueue the send instead. You get 202 right away, and the final delivery status arrives later as a message.status event.

Retrying safely (idempotency)

Send the same request twice — say, after a network timeout — and you risk sending the message twice. To prevent that, pass an Idempotency-Key header with a value you choose. If a request with the same key has already run, the gateway returns the original result instead of sending again.

The key is scoped to your organization, so the same key reused across sessions in the same org counts as the same request.

Rate limits

Each session has two send budgets: a per-minute cap and a per-hour cap. The defaults are 20 per minute and 200 per hour (configurable per session; the server-wide defaults come from DEFAULT_RATE_PER_MIN and DEFAULT_RATE_PER_HOUR).

What happens when you go over depends on the mode:

ModeOver the limit
Sync (default)Returns 429. The error includes retryAfterSeconds.
Async (?async=true)The message stays queued and goes out once the budget refills.

Sending fast or in bulk is exactly the behavior WhatsApp bans numbers for. The rate limits, plus the typing and read-receipt simulation, exist to keep traffic looking human. Keep volume low and pacing natural.

On this page