Package Exports
- @catalisa/wpp-sdk
- @catalisa/wpp-sdk/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@catalisa/wpp-sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
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/sdkQuick 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('admin@example.com', '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: '5511999999999@s.whatsapp.net',
text: 'Hello from SDK!'
});
// Send media
await client.whatsapp.sendMedia({
jid: '5511999999999@s.whatsapp.net',
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('admin@example.com', '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: '5511999999999@s.whatsapp.net',
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.tsTest 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: '5511999999999@s.whatsapp.net',
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: '5511999999999@s.whatsapp.net',
type: 'image',
url: 'https://example.com/photo.jpg',
caption: 'Check this out!'
});
// Send image from base64
await client.whatsapp.sendMedia({
jid: '5511999999999@s.whatsapp.net',
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: '5511999999999@s.whatsapp.net',
type: 'audio',
base64: 'T2dnUwAC...', // Base64 encoded audio
mimetype: 'audio/ogg; codecs=opus'
});
// Send document
await client.whatsapp.sendMedia({
jid: '5511999999999@s.whatsapp.net',
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: '5511999999999@s.whatsapp.net',
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: ['5511111111111@s.whatsapp.net', '5522222222222@s.whatsapp.net']
});
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: ['5533333333333@s.whatsapp.net']
});
// Promote to admin
await client.whatsapp.promoteGroupParticipants({
gid: groupId,
participants: ['5533333333333@s.whatsapp.net']
});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: '120363XXX@g.us', // 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: '5511999999999@s.whatsapp.net',
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: '5511999999999@s.whatsapp.net'
});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: 'user@example.com',
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('admin@example.com', 'password');
// Switch to API token
client.setAuth('wpp_live_abc123', 'apiToken');
// Clear auth
client.clearAuth();API Reference
Authentication
auth.login(email, password)- Login with credentialsauth.forgotPassword(email)- Request password resetauth.resetPassword(token, newPassword)- Reset passwordauth.me()- Get current user
Users (Admin)
users.list()- List all usersusers.get(id)- Get user by IDusers.create(data)- Create userusers.update(id, data)- Update userusers.delete(id)- Delete user
Tenants (Admin)
tenants.list()- List all tenantstenants.get(id)- Get tenant by IDtenants.create(data)- Create tenanttenants.update(id, data)- Update tenanttenants.delete(id)- Delete tenant
API Tokens (Admin)
apiTokens.list(tenantId)- List tokensapiTokens.create(tenantId, data)- Create tokenapiTokens.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 errorAuthError- 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.