All integrations

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.