Skip to main content
Version: v0.7.6

Use an official OpenWA SDK

The OpenWA SDKs are official client libraries that wrap the REST API in a typed, fluent interface. You instantiate one client, point it at your gateway, and call resource methods like client.messages.sendText(...) instead of hand-building HTTP requests, signing headers, and parsing error envelopes.

This page gets you from npm install to a sent WhatsApp message: pick an SDK, install it, and run a verified hello-world.

Prerequisites
  • A running OpenWA gateway. See Installation — the examples assume the local default http://localhost:2785.
  • An API key sent as the X-API-Key header. See Authentication to obtain one.
  • A WhatsApp account you can scan a QR code with from your phone.

Which SDK to use

There are three first-party SDKs. All three are hand-written against the exact API surface (paths, request bodies, response shapes) and unit-tested against a mocked transport that asserts on the precise request path, method, and body — so drift between an SDK method and the real endpoint breaks a test rather than reaching you.

LanguagePackageInstallRequires
JavaScript / TypeScript@rmyndharis/openwanpm install @rmyndharis/openwaNode 18+
Pythonrmyndharis-openwa (imports as openwa)pip install rmyndharis-openwaPython 3.9+
PHPrmyndharis/openwacomposer require rmyndharis/openwaPHP 8.1+, Guzzle 7

They expose the same resource surface; only the language idioms differ — camelCase methods with objects in JS and PHP, snake_case methods with dicts in Python. Examples on this page use the JavaScript/TypeScript SDK; the Python and PHP equivalents are below the hello-world.

The SDKs version independently of the gateway

SDK versions follow SemVer but do not track the gateway version (documented here at v0.7.6). Pin the SDK version your code is tested against, and treat a major SDK bump as potentially breaking.

Who the SDK is for

Use the SDK if you are building an application or automation against an OpenWA gateway — sending messages, managing sessions and groups, configuring webhooks, or working with the WhatsApp Business surfaces. If you would otherwise write fetch/curl calls against the API reference, the SDK gives you the same capabilities plus type safety, percent-encoded path segments, typed errors, and a 30-second per-request timeout out of the box.

It is a request/response client, not an event SDK

The SDK does not open WebSockets, emit events, or expose client.on(...). To receive inbound messages and delivery acks, register a webhook with the webhooks resource and host your own HTTP receiver. See the Webhooks guide.

The resource surface

A single client exposes every user-facing resource as a property. Each maps to one API resource.

ResourceWhat it does
sessionsList, create, start, stop, force-kill, and inspect sessions; fetch QR / pairing codes
messagesSend text, media, location, contacts, templates; reply, forward, react, bulk-send
contactsLook up contacts, check numbers, resolve phone numbers, block / unblock
groupsCreate groups, manage participants, set subject / description, invite codes
webhooksConfigure event delivery to your own HTTP endpoints
chatsList chats, mark read / unread, send typing state
templatesManage reusable message templates
labelsWhatsApp Business labels
channelsWhatsApp Channels / newsletters
catalogWhatsApp Business product catalog
statusWhatsApp Status / stories
healthLiveness / readiness checks

The client also carries two top-level methods: auth() validates the configured key and resolves its role, and request() is a raw escape hatch for any endpoint the resources don't yet wrap.

Operator-only modules are intentionally not in the SDK

Server-side admin modules — docker, metrics, infra, plugins, mcp — are deliberately not exposed through the SDK. Individual methods that still need an OPERATOR-level key (for example creating a session or a webhook) are annotated in the SDK's inline docs; a non-operator key receives 403.

Install

npm install @rmyndharis/openwa

The JavaScript SDK requires Node 18+ because it uses the global fetch. It ships both ESM and CommonJS builds with bundled type definitions, so it works in TypeScript and plain JavaScript without extra setup. On an older runtime, pass your own fetch to the constructor.

Hello world

The flow is: instantiate the client → start a session → authenticate by scanning a QR code → send a message. Starting a session brings the WhatsApp connection up but does not log you in; the session reaches ready only after you scan its QR code from your phone.

import { OpenWAClient } from '@rmyndharis/openwa';

const client = new OpenWAClient({
baseUrl: 'http://localhost:2785',
apiKey: 'YOUR_API_KEY',
});

// 1. Start the session and bring the WhatsApp connection up.
await client.sessions.start('my-session');

// 2. Fetch the QR code and scan it from WhatsApp on your phone
// (Settings → Linked devices → Link a device).
const { qrCode, status } = await client.sessions.getQrCode('my-session');
console.log(status, qrCode); // qrCode is a data:image/png;base64,… URL

// 3. Once the session status is 'ready', send a text message.
const result = await client.messages.sendText('my-session', {
chatId: '628123456789@c.us',
text: 'Hello from the OpenWA SDK!',
});

console.log(result.messageId);

sendText resolves to { messageId, timestamp }, where timestamp is a Unix epoch value in seconds:

{ "messageId": "3EB0XXXXXXXXXXXXXXXX", "timestamp": 1740000000 }

chatId is a WhatsApp JID: 628123456789@c.us for an individual contact and 123456789-123456789@g.us for a group. See the Glossary for the JID format.

The session must be ready before you send

start returns as soon as the engine is initializing — its status will be initializing or qr_ready, not ready. If you call sendText before scanning the QR code, the API returns 409 Conflict (the SDK throws OpenWAConflictError). Poll client.sessions.get('my-session') until status === 'ready', or wait for the webhook session event. See Sessions for the full lifecycle.

If the session already exists you can skip straight to start. To create a fresh one first, call client.sessions.create({ name: 'my-session' }) — this needs an OPERATOR-level key.

Python

from openwa import OpenWAClient

with OpenWAClient(base_url="http://localhost:2785", api_key="YOUR_API_KEY") as client:
client.sessions.start("my-session")

qr = client.sessions.get_qr_code("my-session")
print(qr["status"], qr["qrCode"]) # scan from your phone

# After the session reaches 'ready':
result = client.messages.send_text("my-session", {
"chatId": "628123456789@c.us",
"text": "Hello from the OpenWA Python SDK!",
})
print(result["messageId"])

The Python client is synchronous and returns plain dict/list values. It is also a context manager, so the connection pool is closed for you.

PHP

<?php
require 'vendor/autoload.php';

use OpenWA\Client;

$client = new Client([
'baseUrl' => 'http://localhost:2785',
'apiKey' => 'YOUR_API_KEY',
]);

$client->sessions->start('my-session');

$qr = $client->sessions->getQrCode('my-session');
echo $qr['status'], PHP_EOL; // scan $qr['qrCode'] from your phone

// After the session reaches 'ready':
$result = $client->messages->sendText('my-session', [
'chatId' => '628123456789@c.us',
'text' => 'Hello from the OpenWA PHP SDK!',
]);

echo $result['messageId'];

The PHP entry class is OpenWA\Client; payloads are associative arrays.

Errors you will hit

Every non-2xx response throws a typed error you can branch on. In JavaScript, all error classes extend OpenWAError and are exported, so they are instanceof-checkable; a timeout throws OpenWATimeoutError.

Error class (JS)StatusWhen it happens
OpenWAAuthError401Missing or invalid API key
OpenWAForbiddenError403The key's role is insufficient for an OPERATOR-only route
OpenWANotFoundError404Session, chat, or resource does not exist
OpenWAConflictError409Engine not ready — start the session and scan its QR code first
OpenWARateLimitError429Rate limited — back off and retry
OpenWANotImplementedError501The active engine does not support this operation
OpenWATimeoutErrorThe request exceeded the 30-second timeout
import { OpenWAConflictError, OpenWAAuthError } from '@rmyndharis/openwa';

try {
await client.messages.sendText('my-session', {
chatId: '628123456789@c.us',
text: 'Hi!',
});
} catch (err) {
if (err instanceof OpenWAConflictError) {
// 409 — engine not ready; start the session and scan its QR code first.
} else if (err instanceof OpenWAAuthError) {
// 401 — check your API key.
} else {
throw err;
}
}

The Python (OpenWAConflictError, …) and PHP (OpenWAConflictException, …) hierarchies mirror these one-to-one.

Send the API key over HTTPS in production

X-API-Key is bearer-equivalent — anyone holding it can act as you. Send it only over https:// outside local development. The SDK never follows redirects, so the key is never re-sent to a redirect target. The SDK also does no automatic retries; wrap calls in your own backoff if you need them, especially for 429.

Next steps

  • SDK usage — client configuration, sending media, custom transports, and request patterns
  • Sessions — the full session lifecycle and how to wait for ready
  • Sending messages — every message type and formatting
  • Webhooks — receive inbound messages and delivery events
  • API reference — the underlying REST endpoints the SDK wraps