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

waha-calls

v0.2.1

Published

Client SDK for WhatsApp calls on WAHA (wahacall): config, browser softphone (WebRTC), events and raw PCM. WAHA-native auth, simple event-driven ergonomics.

Readme

waha-calls

Client SDK for WhatsApp calls on WAHA (wahacall). One package: Config (register your AI relay / IVR / reject), Softphone (browser/WebRTC agent panel), Events (incl. the AI→agent handoff), and raw PCM (Node, for a server-side voice bot).

  • Auth = WAHA-native (API key + session).
  • Ergonomia simples e orientada a eventos (offer.accept(), call.mute/end/getStats, .on(...)).
  • Embrulha endpoints que já existem (/api/apps, /ws, /calls/:id/webrtc, /ws/calls/audio).

Contrato completo: docs/estudo-sdk-waha-calls.md.

Install

npm install waha-calls

Connect

import { WahaCalls } from 'waha-calls';

const waha = new WahaCalls({
  baseUrl: 'https://host',
  apiKey: 'KEY',
  session: 'default', // optional — omit for a multi-session client (see below)
});

Config — register the AI relay (or IVR / reject)

await waha.configure({
  dm: { mode: 'relay', waitBeforeAnswer: 3, relay: { url: 'wss://my-bot/voice' } },
  group: { mode: 'reject' },
});

Events + agent handoff

// Incoming call → answer in the softphone
waha.on('offer', async (offer) => {
  const { call } = await offer.accept();   // answers + connects WebRTC audio
  agentAudioElement.srcObject = call!.stream;
});

// AI handed off → agent takes over the same call
waha.on('escalate', async ({ callId, summary }) => {
  showHandoff(summary);
  const { call } = await waha.grab(callId);  // connects WebRTC, displaces the AI
  agentAudioElement.srcObject = call!.stream;
  call!.on('ended', cleanup);
});

Call (active)

call.mute(); call.unmute(); call.end();
call.on('connectionStatus', (s) => {});  // 'connecting' | 'connected' | 'disconnected'
call.on('ended', () => {});
const stats = await call.getStats();      // { rtt, rxLoss, jitterMs, ... }

Node / raw PCM (server-side voice bot)

In Node there is no browser WebSocket, so pass one — install ws and inject it. Then take a live call as raw PCM (no WebRTC):

npm install waha-calls ws
import { WebSocket } from 'ws';
import { WahaCalls } from 'waha-calls';

const waha = new WahaCalls({ baseUrl, apiKey, session, webSocketImpl: WebSocket });

waha.on('call', async ({ id }) => {
  const pcm = await waha.grabPcm(id);
  pcm.onAudio((frame) => stt.write(Buffer.from(frame))); // caller → you
  setInterval(() => pcm.sendAudio(ttsFrame()), 60);      // you → caller
  pcm.on('ended', cleanup);
});

Audio is s16le / 16 kHz / mono — 1920 bytes (960 samples) per 60 ms frame (same framing as the relay). The browser entry point works without webSocketImpl (uses the native WebSocket); the injection is only needed in Node. Full runnable example: examples/node-bot.mjs.

Multi-session (many WhatsApp numbers in one client)

Omit session in the constructor and the client subscribes to all sessions (*) over one events WebSocket. Each event carries its session, and methods take a session argument. Ideal for a multi-tenant backend (e.g. several inboxes/accounts):

const waha = new WahaCalls({ baseUrl, apiKey });   // no session → all of them

waha.on('offer', async (offer) => {
  console.log('ringing on', offer.session, 'from', offer.peer.phone);
  const { call } = await offer.accept();           // offer already knows its session
});

waha.on('escalate', async ({ session, callId }) => {
  const { call } = await waha.grab(callId, undefined, session); // pass the session
});

// config / placeCall / grab / grabPcm take the session explicitly here
await waha.configure({ dm: { mode: 'relay', relay: { url } } }, 'vendas');
await waha.placeCall('[email protected]', 'suporte');

Requires an API key with access to all sessions (a global key). With a single-session key the server scopes * down to that one session automatically. In multi-session mode, calling a method without a session throws — there is no default.

Status

Browser softphone + config + events + handoff (grab) + Node raw-PCM (grabPcm) + multi-session. Zero runtime dependencies. See docs/estudo-sdk-waha-calls.md for the full contract.