Integration
Typed messaging for TypeScript and Node.js
Every call carries a named, exported option type — so your editor completes the fields and your type checker rejects a wrong one before a request is ever built. No runtime dependencies, ESM and CommonJS in the same package.
Install
npm install @waapiapp/sdk
Zero runtime dependencies — it uses the platform's own
fetch. Tested on Node 18, 20 and 22.
Send a message
Configure the instance once on the client and every call inherits it. Any single call can still override it.
import { WaAPI } from "@waapiapp/sdk";
const client = new WaAPI({ token: process.env.WAAPI_TOKEN!, instanceId: 123 });
await client.sendMessage({
chatId: "4915112345678@c.us",
message: "Deployment finished.",
});
The chat ID suffix decides where the message lands:
@c.us for one person,
@g.us for a group,
@newsletter for a channel.
A wrong suffix is accepted and delivers nothing.
The types are the documentation
All 122 client actions are generated from our OpenAPI specification, and so is the option type each one takes. Both are exported, so you can name them, extend them and let the compiler check a payload you assembled elsewhere.
Named option types
import type { SendMediaOptions } from "@waapiapp/sdk";
function buildReport(url: string): SendMediaOptions {
return {
chatId: "4915112345678@c.us",
mediaUrl: url,
mediaCaption: "Nightly report",
};
}
Everything the API can do
await client.createGroup({
groupName: "Ops",
groupParticipants: ["4915112345678@c.us"],
});
await client.getChats({ limit: 50 });
await client.getContacts();
An action we shipped after the last SDK release is reachable by name, so nothing waits on a version bump:
await client.action("some-new-action", { chatId: "4915112345678@c.us" });
Runs wherever your JavaScript runs
No dependencies to audit
The package pulls in nothing at runtime — it uses the
fetch your
platform already has. Nothing to patch when an HTTP library has a CVE, and
nothing added to a cold start.
ESM and CommonJS
import and
require both
work, from the same install. Serverless handlers and older codebases do not
have to migrate first.
Swap the transport
Pass your own fetch
to route through a proxy, add retries, or assert on requests in a test without
a network.
A 200 is not proof it was sent
When a connected account has dropped off, the API answers
200 with an error body.
Code that only checks response.ok
treats that as delivered. The SDK throws on it instead.
import { AuthenticationError, RateLimitError, FailedActionError } from "@waapiapp/sdk";
try {
await client.sendMessage({ chatId, message });
} catch (error) {
if (error instanceof AuthenticationError) { /* token wrong or expired */ }
else if (error instanceof RateLimitError) { await sleep((error.retryAfter ?? 5) * 1000); }
else if (error instanceof FailedActionError) { /* accepted, not carried out */ }
}
ValidationError,
NotFoundError,
ServerError and
ConnectionError cover the rest.
All extend WaAPIError.
TypeScript questions
What the SDK does and where its limits are.
-
No. It is written in TypeScript and ships its own type declarations, but it is published as plain JavaScript for both ESM and CommonJS. A JavaScript project gets working autocomplete from the bundled types without configuring anything.
-
Node 18 and up, because the SDK uses the built-in fetch rather than an HTTP dependency. Every release is tested against 18, 20 and 22. On an older Node you can pass a fetch implementation into the client yourself.
-
Yes. There are no Node-specific APIs and no native modules — it needs fetch and nothing else, which is what Vercel, Cloudflare Workers, Deno and Bun provide.
-
Pass your own fetch into the constructor and assert on the request. That is how the SDK tests itself: 146 cases, none of which touch the network or need a connected account.
-
The methods and their option types are generated from our OpenAPI specification, so a new endpoint becomes a typed method in the next release. Until then it is callable by name through client.action(name, payload).
-
Through webhooks rather than the SDK. Point your instance webhook at an HTTP endpoint in your application; the payload carries the chat ID, which you hand straight back to sendMessage to reply.
-
No. Billing is a flat rate per connected account, regardless of how many messages or API calls you make.
Connect an account and start sending
One flat rate per connected account. Unlimited messages, no approval process, no per-message invoice.