WaSender API
Send WhatsApp messages and receive incoming messages over a simple, authenticated HTTP API. No SDK required.
Introduction
The WaSender API lets your application send WhatsApp messages from a connected number and receive every incoming message as a signed webhook. Requests and responses are JSON over HTTPS.
Base URL
https://www.whatsappbroker.comAuthentication
Each connected WhatsApp number (a session) has its own API token, prefixed with was_. The token identifies both the sending number and its owner, so no login or cookie is needed.
Send it on every request in one of two ways:
- •
Authorization: Bearer <token>(recommended) - •
x-api-key: <token>
Keep tokens secret and use them server-side only. Anyone with a token can send from your number. Rotate it any time from the dashboard.
Send a message
Request body:
| Field | Type | Description |
|---|---|---|
| number* | string | Recipient phone number in international format, digits only (e.g. 14155552671). 5–20 chars. |
| message | string | The text to send — or the caption when media is attached. Up to 4000 characters. |
| mediaUrl | string | Public http(s) URL of a file to send (PDF, image, video, audio, any document). We download it and attach it. Max 16 MB. Preferred for anything large. |
| filename | string | Names the attachment, e.g. "Invoice.pdf". Defaults to the last path segment of mediaUrl — set this when that would be an opaque key, as with signed storage URLs. Wins over media.filename. |
| media | object | Inline file instead of a URL: { data: base64, mimetype, filename? }. Max 16 MB, but hosts often cap request bodies (~4.5 MB) — use mediaUrl for big files. |
Provide at least one of message, mediaUrl or media. Use mediaUrl or media, not both.
Example — cURL
curl -X POST https://www.whatsappbroker.com/api/wa/send \
-H "Authorization: Bearer was_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"number": "14155552671",
"message": "Hello from WaSender 👋"
}'Example — send a PDF or video
# Any file type: PDF, image, video, audio, document.
curl -X POST https://www.whatsappbroker.com/api/wa/send \
-H "Authorization: Bearer was_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"number": "14155552671",
"mediaUrl": "https://example.com/invoice.pdf",
"filename": "Invoice.pdf",
"message": "Here is your invoice 📄"
}'
# filename is optional — without it the name is taken from the URL path.
# Set it when that would be unreadable, e.g. a signed storage URL.
# Or inline base64 instead of a URL (small files only):
# "media": { "data": "JVBERi0xLjQK…", "mimetype": "application/pdf",
# "filename": "invoice.pdf" }Example — JavaScript
const res = await fetch("https://www.whatsappbroker.com/api/wa/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.WA_API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
number: "14155552671", // digits only, with country code
message: "Hello from WaSender 👋",
}),
})
const data = await res.json()
// { ok: true, id: "true_14155552671@c.us_3EB0…" }Success response — 200
{ "ok": true, "id": "true_14155552671@c.us_3EB0…" }Rate limits
Each session has an optional safe-sending throttle that enforces a minimum gap between messages to protect the number from being flagged. When you send too fast, the API responds with 429 and a Retry-After header (seconds).
{
"error": "Too many requests. Wait up to 5s between messages (safe-sending is on).",
"retryAfterSeconds": 5
}Webhooks
Set a webhook URL for a session in the dashboard. WaSender then POSTs a JSON payload to your endpoint for every incoming message.
Example payload
{
"sessionId": "clx0abcd1234",
"from": "14155552671@c.us",
"author": null,
"number": "14155552671",
"numberSerialized": "14155552671@c.us",
"body": "I'd like to place an order",
"type": "chat",
"hasMedia": false,
"isGroup": false,
"timestamp": 1718600000,
"id": "false_14155552671@c.us_3EB0…"
}Every delivery is signed with the session's Webhook Secret using HMAC-SHA256 over the raw request body, in the x-webhook-signature header. Verify it before trusting a request:
import crypto from "crypto"
// rawBody = the exact request body bytes (do not re-serialize)
const signature = req.headers["x-webhook-signature"]
const expected = crypto
.createHmac("sha256", WEBHOOK_SECRET) // this session's Webhook Secret
.update(rawBody)
.digest("hex")
const ok =
signature &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
if (!ok) return res.status(401).end() // reject forged requestsErrors
Errors return a JSON body with an error message and an appropriate HTTP status:
| Status | Meaning |
|---|---|
| 200 | Success — the message was accepted for delivery. |
| 400 | Rejected. Invalid body (missing number, none of message/mediaUrl/media, or both mediaUrl and media); the number's session is not connected; or the mediaUrl could not be fetched, timed out, is not a public http(s) address, or exceeds 16 MB. The error field says which. |
| 401 | Missing or invalid API token. |
| 404 | The session for this token no longer exists. |
| 413 | Request body too large — an inline base64 media payload above the accepted size. Use mediaUrl instead. |
| 429 | Rate limited by safe-sending. Retry after the given delay. |
| 502 | The worker is unreachable or the call timed out. The message may still have been sent, so retry with care. |
Getting your token
- 1Sign in and open the Sessions page in your dashboard.
- 2Create a session and scan the QR to connect your WhatsApp number.
- 3Open the session and copy its API token (starts with was_).
- 4Optionally set a Webhook URL to receive incoming messages.