| POST | /send-message | Send text |
| POST | /send-media-message | Send media by URL |
| POST | /send-media-message | Send media by upload |
| POST | /send-poll-message | Send a poll |
| POST | /send-interactive-message | Send buttons |
| POST | /send-reaction | React to a message |
| POST | /edit-message | Edit a sent message |
| POST | /send-location | Send a location |
| POST | /send-contact | Send a contact card |
| POST | /send-typing | Typing indicator |
| POST | /delete-message | Delete a message |
/send-messageSend text
Plain text, optionally as a reply or with mentions.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
bodyrequired | string | Message text. |
quotedMessageId | string | Reply to this message id. The preview is filled from hosted history when available; unknown ids still send. |
mentions | string[] | User-parts (LID or phone digits) to @-mention. Include @<user-part> in the text so clients highlight it. |
metaData | object | Any JSON you want echoed back. Delivered on the fromMe webhook as meta and stored with the message. {"fromAI": true} also flags the row as AI-sent. |
{
"clientId": "acmemain",
"to": "123456789012345",
"body": "Hi @60111111111111, your order is ready.",
"mentions": ["60111111111111"],
"quotedMessageId": "3EB0…",
"metaData": { "fromAI": false, "userId": "agent_7" }
}{ "status": "sent", "messageId": "3EB0A1B2C3D4E5F6" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
/send-media-messageSend media by URL
JSON mode: we fetch the file from a public URL and send it as image, video, audio, document, voice note or sticker.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
downloadUrlrequired | string | Public https URL. Fetched with a 60 s timeout; 100 MB cap. |
mimeType | string | Overrides the type detected from the response headers / file name. |
caption | string | Ignored for audio (WhatsApp has no audio captions). |
fileName | string | Shown for documents. |
sendAudioAsVoice | boolean | Send audio as a voice note (push-to-talk bubble). Supply OGG/Opus. |
asSticker | boolean | Send a WebP as a sticker. |
quotedMessageId | string | Reply to this message id. The preview is filled from hosted history when available; unknown ids still send. |
mentions | string[] | User-parts (LID or phone digits) to @-mention. Include @<user-part> in the text so clients highlight it. |
metaData | object | Any JSON you want echoed back. Delivered on the fromMe webhook as meta and stored with the message. {"fromAI": true} also flags the row as AI-sent. |
{
"clientId": "acmemain",
"to": "123456789012345",
"downloadUrl": "https://files.example.com/brochure.pdf",
"fileName": "Brochure.pdf",
"caption": "Here is the brochure"
}{ "status": "sent", "messageId": "3EB0…", "mediaType": "document", "bytes": 482113 }
// files over 60 MB are delivered as a text message containing the link:
{ "status": "sent", "messageId": "3EB0…", "fallback": "text" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
502 | The URL could not be fetched (non-200, timeout, or over 100 MB). |
/send-media-messageSend media by upload
Multipart mode: upload the bytes directly. The media kind is inferred from the part's Content-Type (image/*, video/*, audio/*, otherwise document).
Also pass clientId in the URL
For multipart requests the router cannot read the form body, so add ?clientId=… to the URL. JSON routes do not need this.
| Parameter | Type | Description |
|---|---|---|
filerequired | file | The binary, with a correct Content-Type. |
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
caption | string | |
sendAudioAsVoice | "true" | |
asSticker | "true" | |
quotedMessageId | string | |
mentions | string | Comma separated. |
metaData | string | JSON string. |
curl -X POST "https://api.meowsapp.com/send-media-message?clientId=acmemain" \
-H "Authorization: Bearer $SN_TOKEN" \
-F "[email protected];type=image/jpeg" \
-F clientId=acmemain -F to=123456789012345 -F caption="Here you go"{ "status": "sent", "messageId": "3EB0…", "mediaType": "image" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
- Keep files ≤ 64 MB to stay within WhatsApp's own limits; the ingress accepts up to 128 MB.
/send-poll-messageSend a poll
Native WhatsApp poll. Votes come back as vote_update events.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
pollNamerequired | string | The question. |
pollOptionsrequired | string[] | 2–12 options. |
selectableCount | number | How many options a voter may pick. Default 1. |
quotedMessageId | string | Reply to this message id. The preview is filled from hosted history when available; unknown ids still send. |
mentions | string[] | User-parts (LID or phone digits) to @-mention. Include @<user-part> in the text so clients highlight it. |
metaData | object | Any JSON you want echoed back. Delivered on the fromMe webhook as meta and stored with the message. {"fromAI": true} also flags the row as AI-sent. |
{ "clientId": "acmemain", "to": "123456789012345", "pollName": "Lunch?", "pollOptions": ["Pizza", "Sushi"], "selectableCount": 1 }{ "status": "sent", "messageId": "3EB0A1B2C3D4E5F6" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
/send-interactive-messageSend buttons
Native-flow buttons: quick replies, open-URL, call and copy-code. Taps come back as interactive_reply events.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
bodyTextrequired | string | |
footerText | string | |
buttonsrequired | Button[] | {type, displayText, id?, url?, phoneNumber?, copyCode?} — see table below. |
quotedMessageId | string | Reply to this message id. The preview is filled from hosted history when available; unknown ids still send. |
mentions | string[] | User-parts (LID or phone digits) to @-mention. Include @<user-part> in the text so clients highlight it. |
metaData | object | Any JSON you want echoed back. Delivered on the fromMe webhook as meta and stored with the message. {"fromAI": true} also flags the row as AI-sent. |
{
"clientId": "acmemain",
"to": "123456789012345",
"bodyText": "Confirm your viewing on Saturday 3 pm?",
"footerText": "Acme Realty",
"buttons": [
{ "type": "quick_reply", "displayText": "Confirm", "id": "confirm" },
{ "type": "quick_reply", "displayText": "Reschedule", "id": "reschedule" },
{ "type": "cta_url", "displayText": "Directions", "url": "https://maps.example.com/site" },
{ "type": "cta_call", "displayText": "Call agent", "phoneNumber": "+60123456789" },
{ "type": "cta_copy", "displayText": "Copy code", "id": "code", "copyCode": "VIEW-2026" }
]
}{ "status": "sent", "messageId": "3EB0A1B2C3D4E5F6" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
- Button types —
quick_reply:displayText,id·cta_url:displayText,url·cta_call:displayText,phoneNumber·cta_copy:displayText,id,copyCode. - Rendering of interactive messages depends on the recipient's WhatsApp version; keep a text fallback in your flow.
/send-reactionReact to a message
Add or remove an emoji reaction.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
messageIdrequired | string | Target message id. |
emojirequired | string | "" removes your reaction. |
targetFromMe | boolean | Set true when reacting to your own message. |
{ "clientId": "acmemain", "to": "123456789012345", "messageId": "3EB0…", "emoji": "👍" }{ "status": "sent", "messageId": "3EB0…(reaction)", "targetId": "3EB0…" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
/edit-messageEdit a sent message
Edit one of your own text messages. WhatsApp allows edits for roughly 15 minutes after sending.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
messageIdrequired | string | Your message's id. |
bodyrequired | string | New text. |
{ "clientId": "acmemain", "to": "123456789012345", "messageId": "3EB0…", "body": "Corrected: Saturday 4 pm" }{ "status": "sent", "messageId": "3EB0…(edit)", "targetId": "3EB0…" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
/send-locationSend a location
A pinned location with optional label.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
latituderequired | number | |
longituderequired | number | |
name | string | |
address | string | |
quotedMessageId | string | Reply to this message id. The preview is filled from hosted history when available; unknown ids still send. |
mentions | string[] | User-parts (LID or phone digits) to @-mention. Include @<user-part> in the text so clients highlight it. |
metaData | object | Any JSON you want echoed back. Delivered on the fromMe webhook as meta and stored with the message. {"fromAI": true} also flags the row as AI-sent. |
{ "clientId": "acmemain", "to": "123456789012345", "latitude": 3.1579, "longitude": 101.7116, "name": "Acme Gallery", "address": "Jalan Ampang, KL" }{ "status": "sent", "messageId": "3EB0A1B2C3D4E5F6" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
/send-contactSend a contact card
A tappable vCard. Supply phone and we build the card, or pass a full vcard.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
displayNamerequired | string | |
phone | string | E.164 digits. |
vcard | string | Full vCard 3.0 text (overrides phone). |
quotedMessageId | string | Reply to this message id. The preview is filled from hosted history when available; unknown ids still send. |
mentions | string[] | User-parts (LID or phone digits) to @-mention. Include @<user-part> in the text so clients highlight it. |
metaData | object | Any JSON you want echoed back. Delivered on the fromMe webhook as meta and stored with the message. {"fromAI": true} also flags the row as AI-sent. |
{ "clientId": "acmemain", "to": "123456789012345", "displayName": "Acme Sales", "phone": "+60123456789" }{ "status": "sent", "messageId": "3EB0A1B2C3D4E5F6" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
/send-typingTyping indicator
Show or clear "typing…" in the recipient's chat. Send before a reply for a natural feel.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
composing | boolean | Default true; false clears. |
{ "clientId": "acmemain", "to": "123456789012345", "composing": true }{ "status": "ok" }| Status | When |
|---|---|
400 | Missing or invalid fields. |
404 | Unknown or unpaired clientId. |
502 | WhatsApp rejected the send — nothing was delivered. Retry with backoff; check /check-state if it persists. |
/delete-messageDelete a message
Delete for everyone (your own messages only — WhatsApp enforces this) or remove from this device only.
| Parameter | Type | Description |
|---|---|---|
clientIdrequired | string | Your account identifier (letters and digits only). |
torequired | string | Recipient: a LID user-part (123456789012345), a phone with + (+60123456789, resolved to the contact's LID when known), or a full JID (…@lid, …@s.whatsapp.net, groups …@g.us). |
messageIdrequired | string | |
fromMerequired | boolean | Whether the message was sent by this session. |
moderequired | "everyone" | "me" | |
chatRoomId | string | Required for mode: "me" when using hosted history. |
{ "clientId": "acmemain", "to": "123456789012345", "messageId": "3EB0…", "fromMe": true, "mode": "everyone" }{ "status": true }| Status | When |
|---|---|
400 | Bad mode or missing fields. |
404 | Unpaired session, or (mode: me) message not found in history. |
502 | WhatsApp rejected the revoke. |