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

venm-khabri

v1.0.3

Published

Developer-first TypeScript SDK for WhatsApp notifications via OpenWA — session management, message queues, templates, webhooks, and more

Readme

Khabri — WhatsApp API SDK

npm version License: MIT TypeScript

Developer-first TypeScript SDK for WhatsApp notifications via OpenWA.
144+ tests passing · Full TypeScript strict mode · Zero external runtime deps beyond axios/p-queue/zod

Khabri provides a clean, type-safe abstraction over the OpenWA API with built-in support for sessions, messages, queues, notifications, templates, webhooks, retries, and middleware — all designed to work seamlessly in Node.js.

khabri (खबरी) means "informer" or "messenger" in Hindi.


📦 Install

npm install venm-khabri
# or
pnpm add venm-khabri
# or
yarn add venm-khabri

Requirements: Node.js 18+, TypeScript 5+ (for type support), an OpenWA instance and API key.


Features

  • 📱 Session Management — Create, start, stop, force-kill sessions; QR code and pairing code authentication
  • 📤 Send Messages — Text, images, videos, audio, documents, stickers, locations, contacts, templates, replies, forwards, reactions
  • 🔔 Notifications — Pre-built notification templates: OTP, welcome, offers, promotions, rewards, reminders, and more
  • 📋 Queue System — Priority-based message queue with broadcast, batch, pause/resume, and job lifecycle events
  • 🔁 Retry Strategy — Configurable backoff (exponential, fixed, linear), max attempts, and delay
  • 🔗 Webhooks — Full CRUD management + HMAC-SHA256 verified receiver with framework adapters (Express, Fastify, Koa, Hono, NestJS)
  • ⚡ Events & Hooks — Lifecycle hooks (beforeSend, afterSend, beforeWebhook, etc.) and event-driven architecture
  • 👥 Groups — Create, manage participants, update subject/description, invite codes
  • 💬 Chats — List, read/unread, delete, typing indicators, message history
  • 📝 Template Manager — Register, render, and send WhatsApp message templates
  • 🛡️ Full TypeScript — Strict types matching OpenWA API v0.7.5, Zod validation, comprehensive error hierarchy

Installation

# npm
npm install venm-khabri

# pnpm
pnpm add venm-khabri

# yarn
yarn add venm-khabri

Requirements

  • Node.js 18+
  • TypeScript 5+ (for type support)
  • An OpenWA instance and API key

Quick Start

All message operations require an active session. Here's the full flow:

import { Khabri } from "venm-khabri";

const khabri = new Khabri({
  url: "https://openwa.your-instance.com",
  apiKey: "your-api-key",
});

// 1. Create a session
const session = await khabri.sessions.create("my-phone");
// → { id: "abc...", name: "my-phone", status: "created", ... }

// 2. Start the session to get a QR code
const started = await khabri.sessions.start(session.id);
// → { ...status: "qr_ready" }

// 3. Get the QR code (scan with WhatsApp → Linked Devices)
const qr = await khabri.sessions.qr(session.id);
// → { qrCode: "data:image/png;base64,...", status: "qr_ready" }

// 4. Once scanned, session status becomes "ready" — send messages!
const result = await khabri.messages.text(
  session.id,                // session ID
  "[email protected]",       // recipient chat ID
  "Hello from Khabri!",      // message text
);
// → { messageId: "msg_...", timestamp: 1234567890 }

Quick Start with Express

import express from "express";
import { Khabri, createExpressHandler } from "venm-khabri";

const khabri = new Khabri({
  url: process.env.OPENWA_URL!,
  apiKey: process.env.OPENWA_API_KEY!,
});

const app = express();
app.post("/webhook", express.json(), createExpressHandler(khabri.webhooks));
app.listen(4000);

All middleware adapters (createExpressHandler, createFastifyHandler, createKoaHandler, createHonoHandler, NestWebhookService) are exported from the main package entry.


Architecture

┌────────────────────────────────────────────────────────┐
│                    Khabri Client                        │
├────────────────────────────────────────────────────────┤
│  ┌──────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐   │
│  │ Sessions │ │ Messages │ │ Chats  │ │  Groups   │   │
│  │ Manager  │ │  (send)  │ │(CRUD)  │ │ (CRUD)    │   │
│  └──────────┘ └──────────┘ └────────┘ └───────────┘   │
│  ┌──────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐   │
│  │ Contacts │ │  Status  │ │ Queue  │ │ Templates │   │
│  │ (CRUD)   │ │ (stories)│ │Manager │ │  Manager  │   │
│  └──────────┘ └──────────┘ └────────┘ └───────────┘   │
│  ┌──────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐   │
│  │ Webhooks │ │ Notific. │ │ Hooks  │ │  Events   │   │
│  │ CRUD+Recv│ │Templates │ │System  │ │  Emitter  │   │
│  └──────────┘ └──────────┘ └────────┘ └───────────┘   │
│  ┌────────────────────────────────────────────────────┐ │
│  │         Middleware Pipeline                         │ │
│  │  beforeSend → send → afterSend                     │ │
│  │  beforeWebhook → process → afterWebhook            │ │
│  └────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘

All API calls are session-scoped. You must create and start a session before sending messages.


The Session Lifecycle

created → initializing → qr_ready → authenticating → ready
                              ↓                          ↓
                         (scan QR)              disconnected
                              ↓                          ↓
                         authenticating              failed
                              ↓
                           ready
// Create
const session = await khabri.sessions.create("my-phone");

// Start (transitions to qr_ready)
await khabri.sessions.start(session.id);

// Get QR code for scanning
const { qrCode } = await khabri.sessions.qr(session.id);

// Alternative: pairing code (WhatsApp beta)
const { pairingCode } = await khabri.sessions.pairingCode(session.id, "628123456789");

// Stop / disconnect
await khabri.sessions.stop(session.id);

// Force-kill a stuck session
await khabri.sessions.forceKill(session.id);

// Delete permanently
await khabri.sessions.delete(session.id);

// Get session overview stats
const stats = await khabri.sessions.stats();
// { total: 3, active: 1, ready: 1, disconnected: 1, byStatus: {...} }

Modules

Messages

All message methods require a sessionId as the first parameter. Chat IDs use the format {phone}@c.us (e.g., [email protected]).

// Text
await khabri.messages.text(sessionId, "[email protected]", "Hello!");

// Image with caption
await khabri.messages.image(sessionId, "[email protected]", {
  url: "https://example.com/photo.jpg",
  caption: "Check this out!",
});

// Video
await khabri.messages.video(sessionId, "[email protected]", {
  url: "https://example.com/video.mp4",
  caption: "My vacation",
});

// Audio
await khabri.messages.audio(sessionId, "[email protected]", {
  url: "https://example.com/audio.mp3",
});

// Document
await khabri.messages.document(sessionId, "[email protected]", {
  url: "https://example.com/report.pdf",
  filename: "Q3 Report.pdf",
  caption: "Quarterly report",
});

// Sticker
await khabri.messages.sticker(sessionId, "[email protected]", {
  url: "https://example.com/sticker.webp",
});

// Location
await khabri.messages.location(
  sessionId, "[email protected]",
  28.6139, 77.2090,                // lat, lng
  { description: "New Delhi" },
);

// Contact card
await khabri.messages.contact(
  sessionId, "[email protected]",
  "Ravi Kumar", "+919876543210",
);

// Template message
await khabri.messages.template(sessionId, "[email protected]", {
  templateName: "welcome_msg",
  vars: { name: "Anita" },
});

// Reply to a specific message
await khabri.messages.reply(sessionId, "[email protected]", "Got it!", "msg_123");

// Forward a message
await khabri.messages.forward(sessionId, "[email protected]", "msg_123");

// React with emoji
await khabri.messages.react(sessionId, "[email protected]", "msg_123", "👍");

// Delete a message
await khabri.messages.delete(sessionId, "[email protected]", "msg_123");

// Send in bulk
const bulkResult = await khabri.messages.bulk(sessionId, [
  { chatId: "[email protected]", content: { text: "Hello 1" } },
  { chatId: "[email protected]", content: { text: "Hello 2" } },
]);

// Get message history
const history = await khabri.messages.history(sessionId, "[email protected]", {
  limit: 50,
  includeMedia: false,
});

// Send product
await khabri.messages.product(sessionId, "[email protected]", "product_123", {
  catalogId: "catalog_456",
});

Notifications

Pre-built notification templates that use the messages API internally:

// OTP verification
await khabri.notify("otp", { phone: "+1234567890", code: "384729", expiry: "5 minutes" });

// Welcome message
await khabri.notify("welcome", { phone: "+1234567890", name: "Anita" });

// Promotional offer
await khabri.notify("offer", {
  phone: "+1234567890",
  title: "50% Off Weekend Sale",
  description: "Use code WEEKEND50",
  expiry: "48 hours",
  code: "WEEKEND50",
});

// Available: otp, welcome, offer, promotion, reward, reminder,
//            mining-completed, task-completed, custom

Queue

Priority queue with broadcast and batch support:

// Enqueue a message
const job = await khabri.queue.send("+1234567890", "Queued message");
console.log(job.id, job.status); // "queued"

// Broadcast to multiple recipients
const jobs = await khabri.queue.broadcast(["+111", "+222", "+333"], "Hello everyone!");

// Batch send with per-message options
const batch = await khabri.queue.batch([
  { to: "+111", body: "Message 1" },
  { to: "+222", body: "Message 2" },
]);

// Control
khabri.queue.pause();  // Pause processing
khabri.queue.resume(); // Resume processing

// Events
khabri.queue.on("completed", (job) => console.log(`Job ${job.id} done`));
khabri.queue.on("failed", (job, err) => console.error(`Job ${job.id} failed`, err));

// Stats
const stats = khabri.queue.stats();
// { pending: 3, processing: 1, completed: 42, failed: 0, isPaused: false }

Contacts

Session-scoped contact management:

// List contacts
const contacts = await khabri.contacts.list(sessionId);

// Check if a number is registered on WhatsApp
const { exists, registeredName } = await khabri.contacts.check(sessionId, "628123456789");

// Get contact details
const contact = await khabri.contacts.get(sessionId, "contact-id");

// Get profile picture
const { profilePicture } = await khabri.contacts.profilePicture(sessionId, "contact-id");

// Block / unblock
await khabri.contacts.block(sessionId, "contact-id");
await khabri.contacts.unblock(sessionId, "contact-id");

Chats

Session-scoped chat management:

// List chats
const chats = await khabri.chats.list(sessionId);

// Mark read / unread
await khabri.chats.markRead(sessionId, "[email protected]");
await khabri.chats.markUnread(sessionId, "[email protected]");

// Delete a chat
await khabri.chats.delete(sessionId, "[email protected]");

// Send typing indicator
await khabri.chats.sendTyping(sessionId, "[email protected]", "typing");

// Get message history
const messages = await khabri.chats.messages(sessionId, "[email protected]", { limit: 50 });

Groups

Session-scoped group management:

// Create a group
const group = await khabri.groups.create(sessionId, "Team Chat", ["[email protected]"]);

// List groups
const groups = await khabri.groups.list(sessionId);

// Add / remove participants
await khabri.groups.addParticipants(sessionId, groupId, ["[email protected]"]);
await khabri.groups.removeParticipants(sessionId, groupId, ["[email protected]"]);

// Update subject / description
await khabri.groups.updateSubject(sessionId, groupId, "New Name");
await khabri.groups.updateDescription(sessionId, groupId, "New description");

// Leave group
await khabri.groups.leave(sessionId, groupId);

Webhooks

Full CRUD management + verified receiver:

// Create a webhook
const wh = await khabri.webhooks.manager.create(sessionId, {
  url: "https://example.com/webhook",
  events: ["message.received", "message.sent"],
  secret: "my-secret",
  retryCount: 3,
});

// List webhooks
const webhooks = await khabri.webhooks.manager.list(sessionId);

// Update
await khabri.webhooks.manager.update(sessionId, wh.id, { active: false });

// Test
await khabri.webhooks.manager.test(sessionId, wh.id);

// Delete
await khabri.webhooks.manager.delete(sessionId, wh.id);

// Receive webhooks via middleware
app.post("/webhook", express.json(), khabri.webhooks.middleware());

// Listen for webhook events
khabri.on("message.received", (payload) => { /* handle */ });
khabri.on("message.ack", (payload) => { /* handle */ });
khabri.on("session.qr", (payload) => { /* handle */ });
khabri.on("session.authenticated", (payload) => { /* handle */ });
khabri.on("session.disconnected", (payload) => { /* handle */ });

Status / Stories

Send and manage WhatsApp status updates (stories):

// Send text story
await khabri.status.sendText(sessionId, "Good morning!", {
  backgroundColor: "#FF5733",
  font: 1,
});

// Send image story
await khabri.status.sendImage(sessionId, "https://example.com/photo.jpg", {
  caption: "My day!",
});

// List stories
const stories = await khabri.status.list(sessionId);

// Get stories for a contact
const contactStories = await khabri.status.get(sessionId, "[email protected]");

// Delete a story
await khabri.status.delete(sessionId, "status-id");

Hooks & Middleware

Lifecycle hooks for custom logic:

// Before sending (can modify payload)
khabri.beforeSend(async (payload) => {
  payload.body = payload.body.replace("{name}", userName);
  return payload;
});

// After sending
khabri.afterSend(async (result) => {
  console.log("Message sent:", result.id);
});

// Webhook processing pipeline
khabri.beforeWebhook(async (payload) => {
  // Modify or validate webhook payload
  return payload;
});

// Error handling
khabri.onError(async (error) => {
  await errorReportingService.capture(error);
});

Example Apps

Two example applications are included in this repository:

Express Server (apps/express-server)

A REST API server proxying to OpenWA:

cd apps/express-server
cp .env.example .env
# Edit .env with your OpenWA credentials
pnpm dev
# → http://localhost:4000

# Or from root:
pnpm dev:express

| Endpoint | Description | | --------------------------------- | ------------------------------------------------------ | | POST /sessions | Create a new session + get QR code | | GET /sessions | List all sessions | | GET /sessions/:id | Get session details | | POST /sessions/:id/start | Start session (→ qr_ready) | | POST /sessions/:id/stop | Stop/disconnect session | | POST /sessions/:id/force-kill | Force-kill stuck session | | GET /sessions/:id/qr | Get QR code for pairing | | POST /sessions/:id/pairing-code | Request pairing code | | DELETE /sessions/:id | Delete session | | GET /sessions/stats | Session overview statistics | | POST /send | Send text message (requires sessionId + chatId + text) | | POST /send/image | Send image message | | POST /send/video | Send video message | | POST /send/document | Send document message | | POST /send/location | Send location message | | POST /send/contact | Send contact card | | POST /send/template | Send template message | | POST /send/reply | Reply to a message | | POST /notify/:type | Send notification (otp, welcome, etc.) | | POST /queue/send | Enqueue a message | | POST /queue/broadcast | Broadcast to multiple recipients | | GET /queue/stats | Queue statistics | | POST /webhook | Webhook receiver | | GET /events | SSE event stream | | GET /health | Health check |

React Dashboard (apps/react-app)

A full-featured admin dashboard with 10 tabs — all making real API calls (no mock data):

cd apps/react-app
pnpm dev
# → http://localhost:5173

# Or from root:
pnpm dev:react
  • Dashboard — Live API status, session counts, queue stats from /health, /sessions, /queue/stats
  • Send Message — Composer for all 8 message types with sessionId + chatId fields
  • Notifications — Pre-built notification forms with session ID
  • Queue — Queue management with live polling stats
  • Sessions — Create, start, QR code display with polling, stop, delete
  • Templates — Fetch from API, create new, send with sessionId + variables
  • Webhooks — Live webhook event log via SSE
  • Events — Real-time SSE event stream
  • Analytics — Real session stats from /sessions/stats
  • Config — Server URL & API key settings (persisted to localStorage)

Run Both at Once

# From root
pnpm dev:apps
# Starts express-server (port 4000) + react-app (port 5173) in parallel

Authentication

Khabri uses the X-API-Key header for authentication (matching OpenWA API v0.7.5).

const khabri = new Khabri({
  url: "https://openwa.your-instance.com",
  apiKey: "owa_k1_your_api_key_here",
});

Error Handling

Khabri provides a comprehensive error hierarchy:

import { KhabriError, AuthenticationError, ValidationError } from "venm-khabri";

try {
  await khabri.messages.text(sessionId, "invalid", "Hello");
} catch (err) {
  if (err instanceof ValidationError) {
    console.error("Invalid input:", err.details);
  } else if (err instanceof AuthenticationError) {
    console.error("Auth failed:", err.message);
    // err.recoverable === true — retry with new credentials
  } else if (err instanceof RateLimitError) {
    console.error("Rate limited, retry after:", err.retryAfter);
  }
}

| Error Class | Code | Recoverable | Description | | --------------------- | ---------------- | ----------- | ------------------------ | | KhabriError | KHABRI_ERR | — | Base error class | | ConfigurationError | CONFIG_ERR | ✅ | Invalid configuration | | AuthenticationError | AUTH_ERR | ✅ | API key issues | | ValidationError | VALIDATION_ERR | ❌ | Input validation failure | | RateLimitError | RATE_LIMIT | ✅ | Rate limit exceeded | | NetworkError | NETWORK_ERR | ✅ | Network failure | | ApiError | API_ERR | ❌ | API returned error | | TimeoutError | TIMEOUT_ERR | ✅ | Request timed out |


Configuration

interface KhabriConfig {
  url: string;                   // OpenWA API base URL
  apiKey: string;                // Your API key (X-API-Key header)
  timeout?: number;              // Request timeout in ms (default: 30000)
  retries?: number;              // Max retry attempts (default: 3)
  retryDelay?: number;           // Base retry delay in ms (default: 1000)
  queueConcurrency?: number;     // Queue concurrent messages (default: 5)
  logLevel?: "debug"|"info"|"warn"|"error"|"silent"; // (default: "info")
  webhookSecret?: string;        // HMAC secret for webhook verification
}

Development

# Clone the repo
git clone https://github.com/venm-sdk/khabri.git
cd khabri

# Install dependencies
pnpm install

# Build the SDK
pnpm build

# Run tests (144+ tests)
pnpm test

# Type check
pnpm typecheck

# Start example apps
pnpm dev:express   # Express API server on :4000
pnpm dev:react     # React dashboard on :5173
pnpm dev:apps      # Both in parallel

License

MIT © Venm SDK