Skip to content
WaveSenda
Start free
Docs / SDKs & MCP

Libraries

Five clients and three drop-in integrations, all maintained by us. Anything else generates cleanly from the OpenAPI document, which is published from the API itself.

What exists today

PackageStatus
TypeScript@kunshort/wavesendaOfficial. Node 18+, ESM, zero runtime dependencies.
PythonwavesendaOfficial. 3.9+, built on httpx.
PHPwavesenda/wavesenda-phpOfficial. 8.1+, PSR-18 HTTP client.
Gogithub.com/kunshort/wavesenda-goOfficial. 1.21+, nothing outside the standard library.
MCP server@kunshort/wavesenda-mcpOfficial. Read-only by default.
OpenAPI/api/schema/The generated document, for any other language.

Integrations

For the cases where the point is not to write code at all. Each one sends through a number the merchant owns, so the messages arrive from the business rather than from a shortcode nobody recognises. Integrations covers installing and configuring each of them.

PackageWhat it does
n8n@kunshort/n8n-nodes-wavesendaA node plus a trigger, so an inbound message can start a workflow.
WordPresswavesendaChat widget, form notifications and site alerts.
WooCommercewavesenda-for-woocommerceOrder updates and one-shot cart recovery.
ShopifyConnected appThe same for a Shopify store. Nothing to install.

TypeScript

install
npm install @kunshort/wavesenda
usage
import { WaveSenda } from '@kunshort/wavesenda'

const wa = new WaveSenda({
  apiKey: process.env.WAVESENDA_API_KEY!,
  // Retries 5xx, 429 and a disconnected session. Default 2.
  maxRetries: 2,
  timeoutMs: 30_000,
})

const sessions = await wa.sessions.list()
const health = await wa.sessions.health('support')

await wa.messages.send({
  session: 'support',
  to: '+237670000000',
  text: 'Your order is ready for collection.',
  idempotencyKey: 'order-1042-ready',
})

for (const conversation of (await wa.messages.list({ session: 'support' })).data) {
  console.log(conversation.id)
}

Failures throw a WaveSendaError carrying code, status and detail, so the branching you do in the SDK is the same branching you would do against the raw API.

errors
import { WaveSendaError } from '@kunshort/wavesenda'

try {
  await wa.messages.send({ session: 'support', to: number, text })
} catch (error) {
  if (error instanceof WaveSendaError && error.code === 'recipient_not_on_whatsapp') {
    await markNotReachable(number)   // never retry this one
  } else {
    throw error
  }
}

Python

install
pip install wavesenda
usage
import os
from wavesenda import WaveSenda, WaveSendaError

with WaveSenda(api_key=os.environ["WAVESENDA_API_KEY"]) as wa:
    wa.messages.send(
        session="support",
        to="+237670000000",
        text="Your order is ready for collection.",
        idempotency_key="order-1042-ready",
    )

    # Pages for you — follow the cursor without writing the loop.
    for message in wa.messages.iterate(session="support"):
        process(message)

WaveSendaError exposes .retryable, which is true for the transient cases — 5xx, 429 and a gateway that is momentarily unreachable — and false for the ones that will fail identically forever.

Webhook verification

Both SDKs ship the verifier, and both compare in constant time. Give them the raw request bytes: re-serialising the JSON changes the bytes, and the signature will never match.

TypeScript
import { verifyWebhook } from '@kunshort/wavesenda'

const ok = verifyWebhook(rawBody, req.header('X-WaveSenda-Signature'), secret)
Python
from wavesenda import verify_webhook

ok = verify_webhook(raw_body, request.headers.get("X-WaveSenda-Signature"), secret)

MCP — WhatsApp for Claude and other agents

The MCP server exposes your inbox to any MCP client: Claude Desktop, Claude Code, or an agent you have written yourself. It is how you get “summarise what customers asked today” without building anything.

claude_desktop_config.json
{
  "mcpServers": {
    "wavesenda": {
      "command": "npx",
      "args": ["-y", "@kunshort/wavesenda-mcp"],
      "env": { "WAVESENDA_API_KEY": "ws_live_..." }
    }
  }
}
ToolDoes
list_sessionsThe connected numbers, with status and health.
list_conversationsRecent conversations, to find who has been in touch.
read_conversationThe messages in one thread, newest last.
check_numbersWhich numbers have a WhatsApp account.
session_healthThe ban-risk report for one number.
send_messageOnly registered when WAVESENDA_ALLOW_SEND=true.
Read-only by default, deliberately
Sending a WhatsApp message cannot be undone — a customer’s phone buzzes and there is no delete that unsends it from their notification shade. Sending is off until you set WAVESENDA_ALLOW_SEND=true, and even then you can restrict it to particular numbers with WAVESENDA_ALLOWED_SESSIONS. An agent that can read your inbox is useful; one that can message your customers by accident is a liability.
scoped to one number, sending allowed
{
  "mcpServers": {
    "wavesenda": {
      "command": "npx",
      "args": ["-y", "@kunshort/wavesenda-mcp"],
      "env": {
        "WAVESENDA_API_KEY": "ws_live_...",
        "WAVESENDA_ALLOW_SEND": "true",
        "WAVESENDA_ALLOWED_SESSIONS": "support"
      }
    }
  }
}

Any other language

The OpenAPI 3.1 document is served from the API at /api/schema/, with a browsable reference at /api/docs/. It is generated from the code rather than maintained by hand, so it does not drift.

generate a client
npx @openapitools/openapi-generator-cli generate \
  -i https://api.wavesenda.com/api/schema/ \
  -g go \
  -o ./wavesenda-go
SDKs & MCP · WaveSenda Docs