whatshub-client
v1.2.0
Published
Official Node.js client library for WhatsHub - Universal WhatsApp Gateway API with SSE and Webhook support
Maintainers
Readme
whatshub-client
Official Node.js client library for WhatsHub - Universal WhatsApp Gateway API
Features
✅ Easy to use - Simple, intuitive API ✅ TypeScript support - Full type definitions included ✅ Two delivery modes - SSE (persistent servers) or Webhooks (Firebase Functions, serverless) ✅ Real-time messages - Built-in SSE (Server-Sent Events) handling ✅ Webhook support - Perfect for Firebase Functions, AWS Lambda, serverless ✅ Auto-reconnect - Automatic event stream reconnection ✅ Event-driven - EventEmitter-based for reactive programming ✅ Media support - Send images, videos, audio, documents ✅ Auto image optimization - Images over 1MB automatically compressed ✅ Group messaging - Get and message WhatsApp groups ✅ Promise-based - Modern async/await API ✅ HMAC security - Built-in webhook signature verification
Installation
npm install whatshub-clientQuick Start
const WhatsHubClient = require('@phiresky/whatshub-client');
// Create client
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: 'wh_live_your_api_key_here',
sessionId: 'my-company-support'
});
// Create session and connect
await client.createSession();
// Listen for QR code
client.on('qr', (qrCode) => {
console.log('Scan this QR code with WhatsApp:', qrCode);
});
// Listen for connection
client.on('ready', (data) => {
console.log('WhatsApp connected!', data.phoneNumber);
});
// Listen for messages
client.on('message', (message) => {
console.log('New message from:', message.chatId);
console.log('Text:', message.text);
});
// Connect to real-time stream
client.connectSSE();
// Send a message
await client.sendMessage('+27763724011', 'Hello from WhatsHub!');Configuration
Client Options
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub', // Required
apiKey: 'wh_live_xxxxx', // Required
sessionId: 'my-session', // Optional
autoConnect: false, // Optional
debug: false, // Optional
webhookUrl: 'https://your-app.com/webhook', // Optional (for Firebase Functions)
webhookSecret: 'your-secret-123' // Optional (for webhook verification)
});| Option | Type | Required | Description |
|--------|------|----------|-------------|
| hubUrl | string | ✅ Yes | WhatsHub server URL |
| apiKey | string | ✅ Yes | Your WhatsHub API key |
| sessionId | string | ❌ No | Session ID (auto-generated if not provided) |
| autoConnect | boolean | ❌ No | Auto-connect SSE on init (default: false) |
| debug | boolean | ❌ No | Enable debug logging (default: false) |
| webhookUrl | string | ❌ No | Webhook URL for message delivery (Firebase Functions, serverless) |
| webhookSecret | string | ❌ No | Secret for HMAC signature verification |
API Reference
Sessions
createSession(options)
Create a new WhatsApp session
await client.createSession({
sessionId: 'my-session', // Optional
syncContacts: ['+27763724011'], // Optional
syncHistory: false // Optional (default: false)
});Returns: Promise<{sessionId: string}>
getQRCode(sessionId)
Get QR code for WhatsApp authentication
const { qrCode } = await client.getQRCode();
// qrCode is a data URL: "data:image/png;base64,..."Returns: Promise<{qrCode: string}>
getStatus(sessionId)
Get session connection status
const status = await client.getStatus();
console.log(status.connected); // true/false
console.log(status.phoneNumber); // "+27815665778"Returns: Promise<SessionStatus>
getUserStatus()
Get overall user status (all sessions)
const status = await client.getUserStatus();
console.log(status.hasActiveConnection); // true/false
console.log(status.connectedSessions); // 2
console.log(status.sessions); // [...]Returns: Promise<UserStatus>
deleteSession(sessionId)
Delete session (logout from WhatsApp)
await client.deleteSession();Returns: Promise<void>
Messaging
sendMessage(to, message, sessionId)
Send a text message
const result = await client.sendMessage('+27763724011', 'Hello!');
console.log(result.messageId);Parameters:
to(string): Phone number (+27763724011) or group IDmessage(string): Text message contentsessionId(string, optional): Session ID
Returns: Promise<{success: boolean, messageId: string}>
sendMedia(to, media, caption, sessionId)
Send media message (image, video, audio, document)
// From URL
await client.sendMedia('+27763724011', {
url: 'https://example.com/image.jpg',
mimetype: 'image/jpeg',
filename: 'photo.jpg'
}, 'Check this out!');
// From Base64
await client.sendMedia('+27763724011', {
base64: 'iVBORw0KGgoAAAANS...',
mimetype: 'image/png',
filename: 'screenshot.png'
}, 'Screenshot attached');Parameters:
to(string): Phone number or group IDmedia(object): Media object withurlorbase64,mimetype,filenamecaption(string, optional): Media captionsessionId(string, optional): Session ID
Returns: Promise<{success: boolean, messageId: string}>
getMessages(options, sessionId)
Get message history
const messages = await client.getMessages({
contact: '+27763724011', // Optional: filter by contact
from: '2025-11-01', // Optional: start date
to: '2025-11-03', // Optional: end date
limit: 100 // Optional: max messages (default: 100)
});Returns: Promise<Message[]>
Groups
getGroups(sessionId)
Get list of WhatsApp groups
const groups = await client.getGroups();
groups.forEach(group => {
console.log(group.id); // "[email protected]"
console.log(group.subject); // "Family Group"
console.log(group.participants); // 15
});Returns: Promise<WhatsAppGroup[]>
Real-Time Events (SSE)
connectSSE(sessionId)
Connect to Server-Sent Events stream for real-time updates
client.connectSSE();disconnectSSE()
Disconnect from SSE stream
client.disconnectSSE();Events
The client extends EventEmitter and emits the following events:
Connection Events
qr
QR code available for scanning
client.on('qr', (qrCode, data) => {
console.log('QR Code:', qrCode); // data URL
});ready / connected
WhatsApp connected and authenticated
client.on('ready', (data) => {
console.log('Connected!', data.phoneNumber);
});disconnected
WhatsApp disconnected
client.on('disconnected', (data) => {
console.log('Disconnected:', data.reason);
console.log('Will reconnect:', data.shouldReconnect);
});conflict
Multiple devices connected (action required)
client.on('conflict', (data) => {
console.error('Conflict detected:', data.reason);
// Log out of other WhatsApp Web/Desktop sessions
});Message Events
message
New incoming message
client.on('message', (message) => {
console.log('From:', message.chatId);
console.log('Text:', message.text);
console.log('Has media:', message.hasMedia);
// Auto-reply example
if (message.text.toLowerCase().includes('help')) {
client.sendMessage(message.chatId, 'How can I help you?');
}
});message-sent
Message successfully sent
client.on('message-sent', (data) => {
console.log('Message sent to:', data.to);
console.log('Message ID:', data.messageId);
});Other Events
heartbeat
Keep-alive ping (every 30 seconds)
client.on('heartbeat', (data) => {
console.log('Heartbeat:', data.timestamp);
});error
Error occurred
client.on('error', (error) => {
console.error('Error:', error);
});Complete Examples
Example 1: Basic Bot
const WhatsHubClient = require('@phiresky/whatshub-client');
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: process.env.WHATSHUB_API_KEY,
sessionId: 'support-bot',
debug: true
});
async function main() {
// Create session
await client.createSession();
// Handle QR code
client.on('qr', (qrCode) => {
console.log('Please scan this QR code with WhatsApp');
// Display QR code or save to file
});
// Handle connection
client.on('ready', async (data) => {
console.log('✅ WhatsApp connected!', data.phoneNumber);
// Send startup notification
await client.sendMessage('+27763724011', 'Bot is now online! 🤖');
});
// Handle incoming messages
client.on('message', async (message) => {
// Ignore messages from self
if (message.fromMe) return;
console.log(`Message from ${message.chatId}: ${message.text}`);
// Simple command handling
if (message.text === '/help') {
await client.sendMessage(message.chatId,
'Available commands:\n/help - Show this message\n/status - Check bot status');
}
else if (message.text === '/status') {
const status = await client.getUserStatus();
await client.sendMessage(message.chatId,
`Bot Status:\nConnected: ${status.hasActiveConnection}\nSessions: ${status.totalSessions}`);
}
else {
await client.sendMessage(message.chatId,
'Thanks for your message! Type /help for available commands.');
}
});
// Handle disconnection
client.on('disconnected', (data) => {
console.log('Disconnected:', data.reason);
if (data.shouldReconnect) {
console.log('Will attempt to reconnect...');
}
});
// Connect to real-time stream
client.connectSSE();
}
main().catch(console.error);Example 2: Send Bulk Messages
const WhatsHubClient = require('@phiresky/whatshub-client');
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: process.env.WHATSHUB_API_KEY,
sessionId: 'bulk-sender'
});
async function sendBulkMessages() {
// Wait for connection
await client.createSession();
// Get status to ensure connected
const status = await client.getStatus();
if (!status.connected) {
console.log('Not connected. Please scan QR code first.');
return;
}
const contacts = [
'+27763724011',
'+27815665778',
// ... more contacts
];
const message = 'Hello! This is a bulk message from our system.';
for (const contact of contacts) {
try {
await client.sendMessage(contact, message);
console.log(`✅ Sent to ${contact}`);
// Rate limiting: ~40-60 messages/minute
await new Promise(resolve => setTimeout(resolve, 1500));
} catch (error) {
console.error(`❌ Failed to send to ${contact}:`, error.message);
}
}
console.log('Bulk send complete!');
}
sendBulkMessages().catch(console.error);Example 3: Send Media
const WhatsHubClient = require('@phiresky/whatshub-client');
const fs = require('fs');
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: process.env.WHATSHUB_API_KEY,
sessionId: 'media-sender'
});
async function sendImage() {
// From URL
await client.sendMedia('+27763724011', {
url: 'https://example.com/promo.jpg',
mimetype: 'image/jpeg',
filename: 'promo.jpg'
}, '🎉 Special offer just for you!');
// From local file (base64)
const imageBuffer = fs.readFileSync('./image.png');
const base64Image = imageBuffer.toString('base64');
await client.sendMedia('+27763724011', {
base64: base64Image,
mimetype: 'image/png',
filename: 'image.png'
}, 'Check out this screenshot!');
}
sendImage().catch(console.error);Example 4: Group Messaging
const WhatsHubClient = require('@phiresky/whatshub-client');
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: process.env.WHATSHUB_API_KEY,
sessionId: 'group-bot'
});
async function sendToGroup() {
// Get all groups
const groups = await client.getGroups();
console.log(`Found ${groups.length} groups`);
// Find specific group
const targetGroup = groups.find(g =>
g.subject.toLowerCase().includes('team')
);
if (targetGroup) {
await client.sendMessage(targetGroup.id,
'📢 Team announcement: Meeting at 3 PM today!');
console.log(`Message sent to group: ${targetGroup.subject}`);
}
}
sendToGroup().catch(console.error);Webhook Integration (Firebase Functions, Serverless)
For stateless environments like Firebase Functions or AWS Lambda, use webhook mode:
Setup with Webhooks
const functions = require('firebase-functions');
const WhatsHubClient = require('whatshub-client');
// Webhook endpoint - receives messages from WhatsHub
exports.whatsappWebhook = functions.https.onRequest(async (req, res) => {
if (req.method !== 'POST') {
return res.status(405).send('Method Not Allowed');
}
// 1. Verify HMAC signature (CRITICAL!)
const signature = req.headers['x-whatshub-signature'];
const secret = functions.config().whatsapp.webhook_secret;
try {
const isValid = WhatsHubClient.verifyWebhookSignature(
req.body,
signature,
secret
);
if (!isValid) {
return res.status(401).send('Unauthorized');
}
} catch (error) {
return res.status(400).send('Invalid signature');
}
// 2. Parse message
const message = WhatsHubClient.parseWebhookPayload(req.body);
console.log('Received:', message.text);
// 3. Process message (your logic here)
// ...
// 4. Respond quickly (5s timeout)
res.status(200).send('OK');
});
// Setup function - call once to create session
exports.setupWhatsApp = functions.https.onRequest(async (req, res) => {
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: functions.config().whatsapp.api_key
});
const webhookUrl = `https://us-central1-${process.env.GCLOUD_PROJECT}.cloudfunctions.net/whatsappWebhook`;
await client.createSession({
sessionId: 'my-session',
webhookUrl: webhookUrl,
webhookSecret: functions.config().whatsapp.webhook_secret
});
const { qrCode } = await client.getQRCode('my-session');
res.json({ message: 'Scan QR code', qrCode });
});Deploy to Firebase
# Set config
firebase functions:config:set \
whatsapp.api_key="wh_live_your_key" \
whatsapp.webhook_secret="your-secret"
# Deploy
firebase deploy --only functions
# Setup (one-time)
curl https://us-central1-your-project.cloudfunctions.net/setupWhatsAppStatic Utility Methods
The client provides static methods for webhook handling:
WhatsHubClient.verifyWebhookSignature(body, signature, secret)
Verifies HMAC-SHA256 signature from WhatsHub webhooks:
const isValid = WhatsHubClient.verifyWebhookSignature(
req.body, // Request body (object or string)
req.headers['x-whatshub-signature'], // Signature header
process.env.WEBHOOK_SECRET // Your secret
);
if (!isValid) {
throw new Error('Invalid signature');
}WhatsHubClient.parseWebhookPayload(body)
Parses and validates webhook payload:
const message = WhatsHubClient.parseWebhookPayload(req.body);
console.log(message.chatId); // "[email protected]"
console.log(message.text); // "Hello!"
console.log(message.timestamp); // ISO dateTypeScript Usage
import WhatsHubClient, {
WhatsHubClientOptions,
MessageEvent
} from '@phiresky/whatshub-client';
const options: WhatsHubClientOptions = {
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: process.env.WHATSHUB_API_KEY!,
sessionId: 'my-session',
debug: true
};
const client = new WhatsHubClient(options);
client.on('message', (message: MessageEvent) => {
console.log('Message:', message.text);
});
await client.createSession();
client.connectSSE();Error Handling
const client = new WhatsHubClient({
hubUrl: 'https://uat.phiresky.com/api/whatsapp-hub',
apiKey: process.env.WHATSHUB_API_KEY,
sessionId: 'my-session'
});
// Handle errors from API calls
try {
await client.sendMessage('+27763724011', 'Hello!');
} catch (error) {
console.error('Failed to send message:', error.message);
}
// Handle SSE errors
client.on('error', (error) => {
console.error('SSE error:', error);
// Implement retry logic
setTimeout(() => {
console.log('Reconnecting...');
client.connectSSE();
}, 5000);
});
// Handle conflicts
client.on('conflict', (data) => {
console.error('Multiple devices connected!');
console.error('Please log out of other WhatsApp Web/Desktop sessions');
// Notify admin or take corrective action
});Best Practices
1. Use Environment Variables
const client = new WhatsHubClient({
hubUrl: process.env.WHATSHUB_URL,
apiKey: process.env.WHATSHUB_API_KEY,
sessionId: process.env.WHATSHUB_SESSION_ID
});2. Respect Rate Limits
// WhatsApp rate limit: ~40-60 messages/minute
async function sendWithRateLimit(contacts, message) {
for (const contact of contacts) {
await client.sendMessage(contact, message);
await new Promise(resolve => setTimeout(resolve, 1500)); // 1.5s delay
}
}3. Handle Disconnections
client.on('disconnected', (data) => {
if (data.shouldReconnect) {
// Implement exponential backoff
setTimeout(() => client.connectSSE(), 5000);
} else {
// Permanent disconnect - take action
console.error('Permanent disconnect - manual intervention required');
}
});4. Clean Up
process.on('SIGINT', () => {
console.log('Shutting down...');
client.destroy();
process.exit(0);
});Troubleshooting
QR Code Not Appearing
// Ensure session is created first
await client.createSession();
// Then get QR code
const { qrCode } = await client.getQRCode();Messages Not Being Received
// Make sure SSE is connected
client.connectSSE();
// Check connection status
const status = await client.getStatus();
console.log('Connected:', status.connected);Session Keeps Disconnecting
// Check for conflicts
client.on('conflict', (data) => {
console.error('Multiple devices connected!');
// Log out of other WhatsApp Web/Desktop sessions
});API Documentation
For complete API documentation, see:
Support
- Issues: GitHub Issues
- Email: [email protected]
- Documentation: Full API Docs
License
MIT © Phiresky
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
Changelog
See CHANGELOG.md for release history.
Made with ❤️ by Phiresky
