API Reference

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.com

Authentication

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

POST/api/wa/send

Request body:

FieldTypeDescription
number*stringRecipient phone number in international format, digits only (e.g. 14155552671). 5–20 chars.
messagestringThe text to send — or the caption when media is attached. Up to 4000 characters.
mediaUrlstringPublic 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.
filenamestringNames 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.
mediaobjectInline 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
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

cURL — media with a caption
# 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

Node / fetch
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

200 OK
{ "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).

429 Too Many Requests
{
  "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

POST your endpoint
{
  "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:

Verify signature (Node)
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 requests

Errors

Errors return a JSON body with an error message and an appropriate HTTP status:

StatusMeaning
200Success — the message was accepted for delivery.
400Rejected. 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.
401Missing or invalid API token.
404The session for this token no longer exists.
413Request body too large — an inline base64 media payload above the accepted size. Use mediaUrl instead.
429Rate limited by safe-sending. Retry after the given delay.
502The worker is unreachable or the call timed out. The message may still have been sent, so retry with care.

Getting your token

  1. 1Sign in and open the Sessions page in your dashboard.
  2. 2Create a session and scan the QR to connect your WhatsApp number.
  3. 3Open the session and copy its API token (starts with was_).
  4. 4Optionally set a Webhook URL to receive incoming messages.
Go to your sessions