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

@catalisa/wpp-sdk

v0.3.3

Published

TypeScript SDK for WhatsApp Multi-tenant API - Clean, type-safe, and easy to use

Downloads

410

Readme

@wpp/sdk

TypeScript SDK for WhatsApp Multi-tenant API - Clean, type-safe, and easy to use.

Installation

npm install @wpp/sdk
# or
yarn add @wpp/sdk

Quick Start

Admin Client (JWT Authentication)

import { WhatsAppAPIClient } from '@wpp/sdk';

const admin = new WhatsAppAPIClient({
  baseURL: 'https://api.example.com'
});

// Login
const { access_token, user } = await admin.login('[email protected]', 'password');
console.log('Logged in as:', user.email);

// Create tenant
const tenant = await admin.tenants.create({ name: 'Acme Corp' });

// Create API token for tenant
const token = await admin.apiTokens.create(tenant.id, {
  name: 'Production Token',
  isTest: false
});
console.log('API Key:', token.key); // Store this securely!

// Connect device for tenant
await admin.whatsappAdmin.connect(tenant.id);

// Get QR code
const { qr } = await admin.whatsappAdmin.getQR(tenant.id);
console.log('Scan this QR:', qr);

User Client (API Token Authentication)

import { WhatsAppAPIClient } from '@wpp/sdk';

const client = new WhatsAppAPIClient({
  baseURL: 'https://api.example.com',
  auth: {
    type: 'apiToken',
    token: 'wpp_live_abc123_xyz789'
  }
});

// Connect session
const { correlationId } = await client.whatsapp.connect({
  callbackUrl: 'https://myapp.com/webhook'
});

// Send message
await client.whatsapp.sendMessage({
  jid: '[email protected]',
  text: 'Hello from SDK!'
});

// Send media
await client.whatsapp.sendMedia({
  jid: '[email protected]',
  type: 'image',
  url: 'https://example.com/photo.jpg',
  caption: 'Check this out!'
});

// List groups
const groups = await client.whatsapp.listGroups('tenant-id');
console.log('Groups:', groups);

Features

✨ Resource-Based API

Clean, intuitive API organized by domain:

client.auth          // Authentication
client.users         // User management (admin)
client.tenants       // Tenant management (admin)
client.apiTokens     // API token management (admin)
client.whatsapp      // WhatsApp operations (API token auth)
client.whatsappAdmin // WhatsApp admin operations (JWT auth)
client.webhooks      // Webhook management
client.branding      // Branding customization
client.health        // Health check

🔒 Dual Authentication Support

JWT (Admin endpoints)

const client = new WhatsAppAPIClient({
  baseURL: 'https://api.example.com',
  auth: { type: 'jwt', token: 'your-jwt-token' }
});

// Or login to get token automatically
await client.login('[email protected]', 'password');

API Token (WhatsApp endpoints)

const client = new WhatsAppAPIClient({
  baseURL: 'https://api.example.com',
  auth: { type: 'apiToken', token: 'wpp_live_xxx' }
});

📝 Full TypeScript Support

100% typed with IntelliSense support:

import { SendMessageRequest, Group, AdvancedWebhook } from '@wpp/sdk';

const message: SendMessageRequest = {
  jid: '[email protected]',
  text: 'Hello!',
  callbackUrl: 'https://myapp.com/callback'
};

⚠️ Comprehensive Error Handling

import { APIError, AuthError, ValidationError, NotFoundError } from '@wpp/sdk';

try {
  await client.whatsapp.send({ jid: 'invalid', text: 'test' });
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation failed:', error.errors);
  } else if (error instanceof AuthError) {
    console.error('Auth failed, status:', error.statusCode);
  } else if (error instanceof APIError) {
    console.error('API error:', error.message);
  }
}

Testing

The SDK includes comprehensive test suites demonstrating all features:

# Run all tests
npm run test:real          # Complete integration tests
npm run test:messaging     # Messaging operations only
npm run test:groups        # Group operations only
npm run test:tenant        # Specific tenant tests
npm run test:admin-crud    # Admin CRUD (users, tenants)
npm run test:branding      # Branding configuration
npm run test:webhooks      # Basic webhooks

# Or run directly
npx ts-node -r tsconfig-paths/register examples/test-real.ts
npx ts-node -r tsconfig-paths/register examples/test-messaging.ts
npx ts-node -r tsconfig-paths/register examples/test-groups.ts
npx ts-node -r tsconfig-paths/register examples/test-admin-crud.ts
npx ts-node -r tsconfig-paths/register examples/test-branding.ts
npx ts-node -r tsconfig-paths/register examples/test-basic-webhooks.ts

Test Coverage:

  • ✅ Authentication (JWT + API Token)
  • Admin CRUD Operations (users, tenants) - NEW!
  • ✅ Admin Operations (API tokens)
  • ✅ Messaging (text messages)
  • ✅ Group Operations (create, update, lifecycle testing with polling)
  • ✅ Advanced Webhooks (admin CRUD, filtering, logs, stats)
  • Basic Webhooks (CRUD) - NEW!
  • ✅ Device Management (connect, disconnect, reset)
  • Branding Configuration (CRUD) - NEW!
  • ⏭️ Media Operations (deferred to next phase)

Test Results:

| Test File | Results | Coverage | |-----------|---------|----------| | test-real.ts | 19/20 passing (95%) | Admin ops, webhooks, device mgmt | | test-messaging.ts | 7/10 passing (70%, 3 skipped) | Text messages, callbacks | | test-groups.ts | 21/22 passing (95.5%) | Complete group lifecycle | | test-specific-tenant.ts | 7/7 passing (100%) | Device operations | | test-admin-crud.ts | 17/17 passing (100%) 🆕 | User & Tenant CRUD | | test-branding.ts | 6/6 passing (100%) 🆕 | Branding config | | test-basic-webhooks.ts | 8/8 passing (100%) 🆕 | Basic webhook CRUD |

Total Coverage:

  • Endpoints Tested: 70/108 (64.8%) ⬆️ from 53.7%
  • Phase 1 Complete: +12 endpoints tested
  • All new tests: 100% passing 🎉

Test Highlights:

  • ✅ Complete lifecycle testing with proper async operation polling
  • ✅ Automatic cleanup with try/finally blocks
  • ✅ Comprehensive CRUD coverage for admin resources
  • ✅ Multi-webhook testing
  • ✅ User-tenant integration testing

Improvements in test-groups.ts:

  • Added waitForOperation() helper to poll async operations until completion
  • Added checkDeviceReady() helper to verify device status before tests
  • Refactored both user and admin tests with proper polling and group ID extraction
  • Added try/finally cleanup blocks to ensure test groups are deleted
  • Complete lifecycle testing: create → update → verify → cleanup

Examples

Send Message with Callback

const { correlationId } = await client.whatsapp.sendMessage({
  jid: '[email protected]',
  text: 'Hello!',
  callbackUrl: 'https://myapp.com/callback',
  correlationId: 'my-custom-id-123'
});

// Later, check operation status
const operation = await client.whatsapp.getOperation(correlationId);
console.log('Status:', operation.status); // 'pending', 'completed', or 'failed'
console.log('Result:', operation.result);

Media Operations

Sending Media

Send images, videos, audio, and documents via URL or base64:

// Send image from URL
await client.whatsapp.sendMedia({
  jid: '[email protected]',
  type: 'image',
  url: 'https://example.com/photo.jpg',
  caption: 'Check this out!'
});

// Send image from base64
await client.whatsapp.sendMedia({
  jid: '[email protected]',
  type: 'image',
  base64: '/9j/4AAQSkZJRg...', // Base64 encoded image (without data URI prefix)
  mimetype: 'image/jpeg',
  caption: 'Photo uploaded from device'
});

// Send audio (voice message)
await client.whatsapp.sendMedia({
  jid: '[email protected]',
  type: 'audio',
  base64: 'T2dnUwAC...', // Base64 encoded audio
  mimetype: 'audio/ogg; codecs=opus'
});

// Send document
await client.whatsapp.sendMedia({
  jid: '[email protected]',
  type: 'document',
  url: 'https://example.com/contract.pdf',
  filename: 'contract.pdf',
  mimetype: 'application/pdf',
  caption: 'Please review and sign'
});

// Send video
await client.whatsapp.sendMedia({
  jid: '[email protected]',
  type: 'video',
  url: 'https://example.com/video.mp4',
  caption: 'Watch this!'
});

Receiving Media (via Webhooks)

Incoming media is automatically downloaded, decrypted, and included in webhook events:

// Webhook payload structure for messages.upsert with media
interface MessageUpsertEvent {
  event: 'messages.upsert';
  tenantId: string;
  type: 'notify';
  messages: Array<{
    key: {
      remoteJid: string;
      fromMe: boolean;
      id: string;
    };
    message: {
      imageMessage?: {
        url: string;          // WhatsApp CDN URL (encrypted, temporary)
        mimetype: string;     // e.g., 'image/jpeg'
        caption?: string;
        fileSha256: string;
        fileLength: string;
        mediaKey: string;     // Decryption key
      };
      audioMessage?: {
        url: string;
        mimetype: string;     // e.g., 'audio/ogg; codecs=opus'
        seconds: number;      // Duration in seconds
        ptt: boolean;         // True if voice note
        mediaKey: string;
      };
      videoMessage?: { /* similar structure */ };
      documentMessage?: {
        url: string;
        mimetype: string;
        fileName: string;
        mediaKey: string;
      };
    };
    // Automatically downloaded and decrypted media (when downloadMedia=true)
    downloadedMedia?: {
      localPath: string;      // e.g., '/tmp/wpp-media/tenant123/1234567890_abc123.jpg'
      base64: string;         // Base64 encoded content (ready to use)
      mimetype: string;       // e.g., 'image/jpeg'
      type: 'image' | 'video' | 'audio' | 'document' | 'sticker';
    };
  }>;
}

// Example webhook handler
app.post('/webhook', (req, res) => {
  const { event, messages } = req.body;

  if (event === 'messages.upsert') {
    for (const msg of messages) {
      // Check if message has downloaded media
      if (msg.downloadedMedia) {
        const { type, base64, mimetype, localPath } = msg.downloadedMedia;

        console.log(`Received ${type}: ${mimetype}`);
        console.log(`Local path: ${localPath}`);

        // Use base64 directly (e.g., display in frontend)
        const dataUri = `data:${mimetype};base64,${base64}`;

        // Or read from localPath (within Docker container)
        // Note: localPath is only accessible within the worker container
      }

      // Text content (caption for media, or regular text)
      const text = msg.message?.conversation ||
                   msg.message?.extendedTextMessage?.text ||
                   msg.message?.imageMessage?.caption ||
                   msg.message?.audioMessage?.caption;

      if (text) {
        console.log('Text:', text);
      }
    }
  }

  res.status(200).send('OK');
});

Media Download Configuration

Media download is enabled by default per tenant. To disable:

-- Disable automatic media download for a tenant
UPDATE "TenantSettings"
SET "downloadMedia" = false
WHERE "tenantId" = 'your-tenant-id';

Supported Media Types

| Type | Supported Formats | Max Size (recommended) | |------|------------------|------------------------| | image | JPEG, PNG, GIF, WebP | 5 MB | | video | MP4, 3GP | 16 MB | | audio | OGG (Opus), MP3, M4A | 16 MB | | document | PDF, DOC, XLS, etc. | 100 MB | | sticker | WebP | 100 KB |

SendMediaRequest Type

interface SendMediaRequest {
  jid: string;                                      // Recipient JID
  type: 'image' | 'video' | 'audio' | 'document';  // Media type
  url?: string;                                     // URL to download media from
  base64?: string;                                  // Base64 encoded content
  caption?: string;                                 // Caption text
  mimetype?: string;                                // MIME type (auto-detected if URL provided)
  filename?: string;                                // Filename (for documents)
  callbackUrl?: string;                             // Webhook for operation result
  correlationId?: string;                           // Custom tracking ID
}

Group Management

// Create group
const { correlationId } = await client.whatsapp.createGroup({
  subject: 'My Group',
  participants: ['[email protected]', '[email protected]']
});

const operation = await client.whatsapp.getOperation(correlationId);
const groupId = operation.result.gid;

// Update group subject
await client.whatsapp.updateGroupSubject({
  gid: groupId,
  subject: 'New Group Name'
});

// Add participants
await client.whatsapp.addGroupParticipants({
  gid: groupId,
  participants: ['[email protected]']
});

// Promote to admin
await client.whatsapp.promoteGroupParticipants({
  gid: groupId,
  participants: ['[email protected]']
});

Advanced Webhooks

// Create advanced webhook with filtering
const webhook = await client.webhooks.create({
  name: 'Group Messages Only',
  urls: ['https://api.example.com/webhook'],
  eventTypes: ['messages.upsert'],
  filters: {
    remoteJid: '[email protected]', // Specific group
    fromMe: false,                // Only incoming messages
    textContains: 'urgent'        // Only messages containing 'urgent'
  },
  secret: 'my-webhook-secret',
  retryCount: 3,
  timeoutMs: 10000
});

// Get webhook statistics
const stats = await client.webhooks.getStats(webhook.id);
console.log('Trigger count:', stats.triggerCount);
console.log('Success rate:', stats.successRate);
console.log('Avg response time:', stats.avgResponseTimeMs);

// Get delivery logs
const logs = await client.webhooks.getLogs(webhook.id, {
  success: false, // Only failed deliveries
  limit: 50
});

Admin Operations

// List all devices
const devices = await admin.whatsappAdmin.listDevices();

// Connect device for specific tenant
await admin.whatsappAdmin.connect('tenant-123');

// Send message as tenant
await admin.whatsappAdmin.sendMessage('tenant-123', {
  jid: '[email protected]',
  text: 'Admin sending message as tenant'
});

// Generate QR link for support
const qrLink = await admin.whatsappAdmin.generateQRLink('tenant-123', {
  expirationHours: 24
});
console.log('Share this link:', qrLink.url);

// Send QR link to user via WhatsApp
await admin.whatsappAdmin.sendQRLink('tenant-123', {
  recipientTenantId: 'tenant-456',
  recipientJid: '[email protected]'
});

Branding Customization

// Update branding for tenant
await admin.branding.update({
  tenantId: 'tenant-123',
  companyName: 'Acme Corp',
  primaryColor: '#FF5722',
  logoUrl: 'https://example.com/logo.png',
  headerMessage: 'Welcome to Acme Support',
  footerMessage: 'Powered by Acme Corp'
});

// Get public branding (no auth required)
const branding = await client.branding.get('tenant-123');

User Management

// Create user
const user = await admin.users.create({
  email: '[email protected]',
  password: 'securepassword',
  role: 'user',
  tenantId: 'tenant-123'
});

// List all users
const users = await admin.users.list();

// Update user role
await admin.users.update(user.id, {
  role: 'admin'
});

// Delete user
await admin.users.delete(user.id);

Health Check

const health = await client.health.check();
console.log('API Status:', health.status); // 'ok'

Configuration

Advanced Configuration

const client = new WhatsAppAPIClient({
  baseURL: 'https://api.example.com',
  timeout: 60000, // 60 seconds (default: 30000)

  // Interceptors
  onRequest: async (config) => {
    console.log('Request:', config.method, config.url);
    return config;
  },

  onResponse: async (response) => {
    console.log('Response:', response.status);
    return response;
  },

  onError: (error) => {
    console.error('API Error:', error.message);
  }
});

Dynamic Auth Updates

const client = new WhatsAppAPIClient({
  baseURL: 'https://api.example.com'
});

// Login and set JWT
await client.login('[email protected]', 'password');

// Switch to API token
client.setAuth('wpp_live_abc123', 'apiToken');

// Clear auth
client.clearAuth();

API Reference

Authentication

  • auth.login(email, password) - Login with credentials
  • auth.forgotPassword(email) - Request password reset
  • auth.resetPassword(token, newPassword) - Reset password
  • auth.me() - Get current user

Users (Admin)

  • users.list() - List all users
  • users.get(id) - Get user by ID
  • users.create(data) - Create user
  • users.update(id, data) - Update user
  • users.delete(id) - Delete user

Tenants (Admin)

  • tenants.list() - List all tenants
  • tenants.get(id) - Get tenant by ID
  • tenants.create(data) - Create tenant
  • tenants.update(id, data) - Update tenant
  • tenants.delete(id) - Delete tenant

API Tokens (Admin)

  • apiTokens.list(tenantId) - List tokens
  • apiTokens.create(tenantId, data) - Create token
  • apiTokens.delete(tenantId, tokenId) - Delete token

WhatsApp (User)

  • Session: connect(), reconnect(), reset(), logout(), ping()
  • Messaging: sendMessage(), sendReaction()
  • Media: sendMedia({ type, url?, base64?, caption?, mimetype?, filename? })
  • Groups: createGroup(), updateGroupSubject(), addGroupParticipants(), etc.
  • Queries: listGroups(), listChats(), listContacts(), getOperation()
  • Pairing: requestPairingCode(), getPairingCode()
  • Driver Config: configureDriver(), getDriverConfig()

WhatsApp Admin

  • Device: listDevices(), getDevice(), getQR()
  • Session: connect(), reconnect(), disconnect(), reset(), ping()
  • Operations: sendMessage(), getOperation()
  • Groups: listGroups(), createGroup(), manageGroupParticipants(), etc.

Webhooks

  • Basic: createBasic(), listBasic(), deleteBasic()
  • Advanced (Admin): createAdvanced(), listAdvanced(), updateAdvanced(), deleteAdvanced()
  • Advanced (User): create(), list(), update(), delete()
  • Logs & Stats: getLogs(), getStats()

Branding

  • get(tenantId) - Get branding (public)
  • update(data) - Update branding (admin)
  • delete(tenantId) - Delete branding (admin)

Health

  • check() - Health check

Error Types

  • APIError - Base API error
  • AuthError - Authentication/authorization errors (401, 403)
  • ValidationError - Validation errors (400, 422)
  • NotFoundError - Not found errors (404)
  • RateLimitError - Rate limit errors (429)
  • NetworkError - Network/connection errors

License

MIT

Support

For issues and questions, visit the GitHub repository.