# Send WhatsApp Messages from Node.js

> Send a WhatsApp template and a text message from Node.js with the whabuzz npm package, verify signed webhooks and handle errors, with working code.

Source: https://whabuzz.com/blog/whatsapp-api-nodejs/
By Smaran Challapalli. Published 16 Oct 2026. Updated 16 Oct 2026.

The `whabuzz` npm package is a Node.js client for the WhaBuzz WhatsApp API. It has no dependencies, uses `fetch`, ships ESM, CommonJS and TypeScript types, and passes Meta's own request and response bodies through unchanged. This post sends a template and a text message, verifies a webhook and handles errors.

## What do you need before you start?

You need a WhaBuzz API key, one connected WhatsApp number, and Node 18 or newer. Per the [Node.js SDK page](https://docs.whabuzz.com/sdks/node/), the package targets Node 18+ and edge runtimes. As of 16 Oct 2026 the latest version on npm is 0.1.1, so expect the surface to move as it matures. Pin the version you test against.

```bash
npm install whabuzz
export WHABUZZ_API_KEY="wb_live_..."   # your key
```

The client reads `WHABUZZ_API_KEY` from the environment. You can also pass `new WhaBuzz({ apiKey })`. The constructor takes `apiKey`, `baseUrl`, `tenant`, `fetch` and `timeoutMs`, and the default timeout in the SDK source is 30 seconds. Use the sandbox with test keys while you build. Connecting a number is covered in the [getting started guide](https://docs.whabuzz.com/getting-started/).

## How do you send a text message from Node.js?

Call `wb.messages.send` with Meta's `/messages` body plus `from`. Per the [sending messages guide](https://docs.whabuzz.com/guides/messages/), WhaBuzz does not have its own message format, and `from` is the `phone_number_id` of one of your numbers or its E.164 number.

```js
import { WhaBuzz } from 'whabuzz';

const wb = new WhaBuzz(); // reads WHABUZZ_API_KEY
const res = await wb.messages.send({
  from: '+14692311132', // your connected number
  to: process.env.TO, // the recipient, E.164
  type: 'text',
  text: { body: 'Hello from WhaBuzz' },
});
console.log(res.messages[0].id);
```

The SDK adds `messaging_product: 'whatsapp'` for you. The response is Meta's, so `res.messages[0].id` is the `wamid`. That id is what later `message.status` events refer to.

A free-form text only works inside the 24-hour customer window, meaning the customer messaged you in the last 24 hours. WhaBuzz does not check the window. Meta decides, and if Meta refuses, you see Meta's error unchanged.

One more behaviour from the docs: a busy number can answer `202 {"status":"queued"}`, and the message then goes out in order. Do not assume the response always contains `messages[0]`.

## How do you send a template message?

Use `type: 'template'` and Meta's template object. To start a conversation, WhatsApp requires an approved template. The [templates guide](https://docs.whabuzz.com/guides/templates/) covers creating one and waiting for approval. Once it is approved, this is the same shape the getting started guide shows with `curl`:

```js
const res = await wb.messages.send(
  {
    from: '+14692311132',
    to: process.env.TO,
    type: 'template',
    template: {
      name: 'renewal_reminder',
      language: { code: 'en' },
      components: [
        {
          type: 'body',
          parameters: [
            { type: 'text', text: 'Kerry' },
            { type: 'text', text: '31 Oct' },
          ],
        },
      ],
    },
  },
  { idempotencyKey: 'renewal-48213-2026-10' },
);
```

`renewal_reminder` is the example template name from the docs. Use the name of a template approved on your own account.

The second argument carries `idempotencyKey`. It becomes the `Idempotency-Key` header, so a retry will not send twice. The rules are in [Stop duplicate WhatsApp API messages](https://whabuzz.com/blog/whatsapp-api-idempotency/). Make the key from the business event, and store it with your job before the first attempt.

## Which methods does the SDK have?

Every method below exists in the package source at `sdks/node`, and the Node.js SDK page lists the same surface.

| Resource | Methods |
|---|---|
| `wb.tenant` | `get` |
| `wb.messages` | `send`, `batch`, `getBatch`, `listBatchItems`, `cancelBatch`, `markRead` |
| `wb.templates` | `list`, `create`, `get` |
| `wb.numbers` | `list`, `get` |
| `wb.webhooks.endpoints` | `list`, `create`, `get`, `update`, `delete`, `verify`, `rotateSecret` |
| `wb.events` | `list`, `get`, `iterate` |
| `wb.media` | `get` |

For any route without a helper, call `wb.request({ method, path })`. Platform keys that act for many businesses pass `tenant` in the constructor or per call. See the [tenants guide](https://docs.whabuzz.com/guides/tenants/).

## How do you handle errors in Node.js?

Failed calls throw `WhaBuzzError`. It has `status`, `source`, `code`, `meta`, `details`, `requestId` and `body`. Check `source` first, because it tells you who produced the error. Per the [errors page](https://docs.whabuzz.com/errors/), `meta` means Meta answered with an error and WhaBuzz returns it verbatim, and `whabuzz` means WhaBuzz stopped the request. The SDK adds `client` for requests that never completed, such as a network failure or timeout.

```js
import { WhaBuzzError } from 'whabuzz';

try {
  await wb.messages.send(body, { idempotencyKey: key });
} catch (e) {
  if (!(e instanceof WhaBuzzError)) throw e;
  if (e.source === 'meta') {
    console.error('Meta refused', e.meta.code, e.meta.message, e.requestId);
  } else if (e.source === 'client') {
    // timeout or connection error: the outcome is unknown, retry with the SAME key
  } else {
    console.error('WhaBuzz', e.status, e.code, e.requestId);
  }
}
```

Three rules for the branches:

1. **Log `requestId`.** Every response has a `request_id`, and you can look it up with `GET /v1/requests/{request_id}`.
2. **Do not auto-retry Meta 4xx.** The errors page says 4xx means your request or account state.
3. **Retry unknown outcomes with the same key.** A `client` error, or a `502 meta_unreachable`, means the message may or may not have gone out.

Branch on `error.code` for Meta errors, for example `131047` when the 24-hour window is closed and you need a template.

## How do you verify a webhook in Node.js?

Use `verifyWebhook` on the exact bytes you received, before any JSON parsing. It checks the `X-WhaBuzz-Signature` header and returns the parsed event.

```js
import express from 'express';
import { verifyWebhook, WebhookVerificationError } from 'whabuzz';

const app = express();

app.post('/whatsapp/events', express.raw({ type: '*/*' }), async (req, res) => {
  try {
    const event = await verifyWebhook(req.body, req.headers, process.env.WHABUZZ_WEBHOOK_SECRET);
    // store event.dedupe_key first, then process
    res.sendStatus(200);
  } catch (e) {
    if (e instanceof WebhookVerificationError) return res.status(400).send(e.reason);
    throw e;
  }
});
```

`express.raw` keeps the body as a `Buffer`, which `verifyWebhook` accepts. The error carries a `reason`: `missing_header`, `malformed_header`, `timestamp_out_of_tolerance`, `signature_mismatch` or `invalid_json`. The timestamp tolerance is 300 seconds.

Verification is the first step, not the last. Deliveries are at least once, so dedupe on `dedupe_key` as described in [WhatsApp webhooks that never lose an event](https://whabuzz.com/blog/whatsapp-webhook-reliability/). Express is only an example. The function also runs on Workers, Deno and Bun, per the SDK source.

## Frequently asked questions

### Does the whabuzz package have dependencies?

No. Per the Node.js SDK page it has zero dependencies and uses `fetch`.

### Which Node versions does it support?

Node 18 and newer, plus edge runtimes.

### Does the SDK retry failed requests for me?

The source has no automatic retry. A timeout raises a `client` error. Retry yourself, with the same `idempotencyKey`.

### Can I use the SDK from TypeScript?

Yes. The package ships full TypeScript types.

### Is there a command line tool?

Yes. The same package ships the `whabuzz` CLI. See the [CLI page](https://docs.whabuzz.com/sdks/cli/).

## Related reading

- [Node.js SDK reference](https://docs.whabuzz.com/sdks/node/) and the [sending messages guide](https://docs.whabuzz.com/guides/messages/).
- [Stop duplicate WhatsApp API messages](https://whabuzz.com/blog/whatsapp-api-idempotency/) for retry-safe sends.
- [WhatsApp webhooks that never lose an event](https://whabuzz.com/blog/whatsapp-webhook-reliability/) for the receiving side.
- [All guides by task](https://whabuzz.com/guides/) and the [WhatsApp API overview](https://whabuzz.com/whatsapp-api/).
