npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

whatsapp-cloud-client

v0.2.0

Published

A typed WhatsApp Cloud API client for Node: webhook signature verification first, in constant time and fail-closed.

Readme

whatsapp-cloud-client

A typed WhatsApp Cloud API client for Node. Webhook signature verification first, in constant time and fail-closed.

npm license types

Install

npm i whatsapp-cloud-client

It ships TypeScript sources, not compiled JavaScript, so it needs a TypeScript-aware runtime or bundler — Bun, tsx, Vite, Next, esbuild. That is a deliberate choice for now and not an oversight: a compiled build is worth adding, and it is not worth pretending to have.

Example

import { verifyMetaSignature, parseMetaWebhook } from "whatsapp-cloud-client/webhook";

// In your webhook handler. The RAW body — not the parsed one: re-serialising changes the bytes and
// the signature stops matching, which is the single most common way this check silently never passes.
export async function handler(rawBody: string, headers: Headers) {
  if (!verifyMetaSignature(rawBody, headers.get("x-hub-signature-256"), process.env.META_APP_SECRET ?? "")) {
    return new Response("bad signature", { status: 401 });
  }

  const { inbound, statuses } = parseMetaWebhook(JSON.parse(rawBody));

  for (const message of inbound) {
    // message.from is E.164 without the '+', message.body is the text,
    // message.media is present when it carried an image, document, audio or video.
    console.log(message.from, message.body);
  }
  for (const status of statuses) {
    console.log(status.providerMessageId, status.status); // sent | delivered | read | failed
  }
  return new Response("ok");
}

What it does

  • Verifies the webhook signature over the raw body, in constant time, and answers false when the header or the secret is missing — never a pass by omission.
  • Parses the webhook tolerantly: an unexpected shape yields empty sections instead of throwing, so one malformed message never costs you the rest of the batch.
  • Sends text, templates, media and interactive messages, and uploads media to the Cloud API.
  • Classifies Meta's errors into what is worth retrying and what is not — including the 400s that are really rate limiting in disguise, and the revoked token that no retry will ever fix.
  • Does not: manage your tenants, store anything, or decide when to send. It talks to Meta.

Why it exists

Most of the code you write against the WhatsApp Cloud API is not sending messages — it is the part around it. Verifying that a webhook is really from Meta. Deciding whether a failure is worth retrying. Discovering that a media download is two calls and that the second one is not JSON. Finding out that a button parameter must be the link suffix, because Meta concatenates it onto the base it stored — and that it does not fail on send: the message is accepted, the row says delivered, and the link only breaks when the customer taps it.

This package is that part, extracted from a system where it runs against live customer conversations.

Design notes

The signature check is fail-closed and takes the raw body. Every other design leaks: comparing with === leaks timing, and re-serialising the parsed body changes the bytes so the check silently never passes — which looks exactly like "it works" until someone forges a request.

Errors are classified by status and code, never by message text. Meta rewrites its English without notice, and a guard that matches on text goes quietly dead at the next rewording. The default for an unknown code is transient, and that is deliberate: a false terminal loses the customer's message forever, while a false transient spends a few API calls and ends up in a dead-letter queue anyway. The two mistakes do not cost the same.

Contributing

See CONTRIBUTING.md — it says in the first line whether your pull request will be considered.

Security

See SECURITY.md.

License

Apache-2.0. See LICENSE and AUTHORS.


Built by Vorluno, extracted from niiko — where it runs in production. Unofficial: not affiliated with, endorsed by, or sponsored by Meta.