📱 WhatsApp Multi-Driver API v1.0

Complete Developer Guide - Multi-Driver WhatsApp Integration

✅ V1.0 ✅ MULTI-DRIVER ✅ MCP ENABLED 📱 PRODUCTION READY 🔔 WEBHOOKS

Welcome to the WhatsApp Multi-Driver API documentation. This API provides WhatsApp integration through two drivers: WWebJS (WhatsApp Web.js) and Cloud API (Meta's official Business API). Choose the driver that best fits your use case.

Two Integration Methods:
  • REST API - Traditional HTTP endpoints for all operations
  • MCP (Model Context Protocol) - AI-friendly interface for Claude and other AI agents

🚀 Quick Start (5 minutes)

Get up and running with the WhatsApp Multi-Driver API in 5 minutes.

Step 1: Get API Key

Contact your system administrator to obtain an API key. The API key is required for all API calls and should be stored securely.

âš ī¸ Security: Never commit API keys to version control or expose them in client-side code. Use environment variables or secure key management systems.

Step 2: Test Connection

// Test API connection
const response = await fetch('https://apiwts.top/health', {
  headers: {
    'X-API-Key': 'your-api-key-here'
  }
});

const data = await response.json();
console.log('API Status:', data.status); // Should be "healthy"

Step 3: Create WhatsApp Session

// Create a new WhatsApp session
const response = await fetch('https://apiwts.top/api/v1/sessions', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key-here',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-first-session'
  })
});

const session = await response.json();
console.log('Session created:', session);

Step 4: Get QR Code

// Get QR code to scan with WhatsApp
const response = await fetch('https://apiwts.top/api/v1/sessions/my-first-session/qr', {
  headers: {
    'X-API-Key': 'your-api-key-here'
  }
});

const blob = await response.blob();
const qrUrl = URL.createObjectURL(blob);

// Display QR code in an img tag
document.getElementById('qr-code').src = qrUrl;

Step 5: Send Your First Message

// Send WhatsApp message
const response = await fetch('https://apiwts.top/api/v1/messages/text', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key-here',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-first-session',
    to: '+5511999999999', // International format
    text: 'Hello from WhatsApp API! 👋'
  })
});

const result = await response.json();
console.log('Message sent:', result.id);
✅ You're ready! Continue reading for complete endpoint documentation and advanced features.

âš ī¸ CORS Setup - Critical Configuration

âš ī¸ CRITICAL: CORS setup is required for web applications. Without it, your requests will fail with CORS errors.

Why CORS Matters

  • Browser Security: Browsers block direct requests to external domains
  • API Protection: External APIs often don't allow all origins
  • Development vs Production: Different solutions needed for each environment

Development Solution: Vite Proxy

For development with Vite, use proxy configuration:

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': {
        target: 'https://apiwts.top',
        changeOrigin: true,
        secure: false
      },
      '/health': {
        target: 'https://apiwts.top',
        changeOrigin: true,
        secure: false
      }
    }
  }
});

Code Implementation

// ✅ CORRECT - Use relative paths (works with proxy)
const response = await fetch('/api/v1/sessions/my-session/qr', {
  headers: { 'X-API-Key': 'your-key' }
});

// ❌ WRONG - Direct external URL (CORS error)
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session/qr', {
  headers: { 'X-API-Key': 'your-key' }
});

🔑 Authentication

All API requests require authentication using an API Key in the X-API-Key header.

Getting Your API Key

Contact your system administrator to obtain an API key for your application. Each API key is unique and provides access to the API resources.

âš ī¸ CRITICAL: Store your API key securely. Never expose it in client-side code, public repositories, or logs. Treat it like a password.
📋 API Key Format:
  • Production: sk_live_{64-character-hex}
  • Test: sk_test_{64-character-hex}
  • Total Length: 72 characters
  • Example: sk_live_a1b2c3d4e5f6789... (shortened for display)

Security Note: API keys are generated with 256 bits of cryptographic randomness and stored as SHA-256 hashes in the database.

Using the API Key

// Include API Key in all requests
const headers = {
  'X-API-Key': 'your-api-key-here',
  'Content-Type': 'application/json'
};

const response = await fetch('https://apiwts.top/api/v1/sessions', {
  headers
});
âš ī¸ Security: Never commit API keys to version control. Use environment variables.

📊 Driver Comparison

This API supports two WhatsApp integration drivers. Choose based on your requirements:

Available Drivers

Driver Type Best For
WWebJS WWebJS WhatsApp Web.js (Puppeteer) Full feature access, personal/business accounts, no Meta approval needed
Cloud API Cloud API Meta Official Business API Enterprise scale, template messages, official support, requires Meta approval
Instagram Instagram Meta Graph API (Instagram Messaging) Instagram DM automation, business accounts, requires Meta App approval (v3.9.0+)

Feature Compatibility Matrix

Feature WWebJS Cloud API Notes
Text Messages ✅ ✅ Full support on both
Media Messages (Image, Video, Audio, Document) ✅ ✅ Full support on both
Template Messages ❌ ✅ Requires Meta-approved templates
Message Reactions ✅ ❌ Emoji reactions to messages
Message Revoke/Delete ✅ ❌ ~48 hour window
Forward Messages ✅ ❌ Forward existing messages
Edit Messages ✅ ❌ ~15 min window (v3.4+)
Download Media ✅ ❌ Base64 from messages (v3.4+)
Send Location ✅ ✅ Geographic coordinates (v3.4+)
Get Quoted Message ✅ ❌ Retrieve replied-to message (v3.4+)
Star/Unstar Messages ✅ ❌ Favorite/unfavorite messages (v3.4+)
Pin/Unpin Messages ✅ ❌ Pin messages in chat for duration (v3.4+)
Get Message Mentions ✅ ❌ Get @mentions in a message (v3.4+)
Get Poll Votes ✅ ❌ Get votes from poll messages (v3.4+)
Message Info (Receipts) ✅ ❌ Detailed delivery/read info
Contact Management ✅ ❌ List, block, unblock contacts
Chat Management ✅ ❌ Archive, mute, pin, typing indicators
Groups (Basic) ✅ ✅ Create, info, add/remove participants
Groups (Admin) ✅ ❌ Promote/demote admins, settings
Scheduled Messages ✅ ✅ Future message scheduling
Webhooks ✅ ✅ Real-time notifications
QR Code Authentication ✅ ❌ Cloud API uses phone number verification

When to Use Each Driver

Use WWebJS when:
  • You need full feature access (reactions, revoke, contacts, chats)
  • You want to use personal or business WhatsApp accounts
  • You don't want to go through Meta's approval process
  • You need group admin operations
Use Cloud API when:
  • You need enterprise-scale messaging
  • You want to use pre-approved message templates
  • You require official Meta support
  • Your use case is approved by Meta

Specifying Driver in Requests

When creating a session, specify the driver in the configuration:

// Create session with WWebJS driver (default)
POST /api/v1/sessions
{
  "sessionId": "my-session",
  "config": {
    "driver": "wwebjs"
  }
}

// Create session with Cloud API driver
POST /api/v1/sessions
{
  "sessionId": "my-cloud-session",
  "config": {
    "driver": "cloud-api",
    "phoneNumberId": "your-phone-number-id",
    "accessToken": "your-meta-access-token"
  }
}
Important: Throughout this documentation, features marked with driver badges indicate compatibility:
  • WWebJS - Available only with WWebJS driver
  • Cloud API - Available only with Cloud API driver
  • Instagram - Available only with Instagram driver (v3.9.0+)
  • Both - Available with both WhatsApp drivers

🔗 REST API Reference

Health Check

GET /monitoring/health

Verify API connectivity and status (comprehensive health check).

// Request (no authentication required)
const response = await fetch('https://apiwts.top/monitoring/health');

// Response 200
{
  "status": "healthy",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "uptime": 1186.06,
  "version": "1.0.0",
  "environment": "production",
  "checks": {
    "database": "healthy",
    "cache": "healthy",
    "messageQueue": "healthy",
    "sessionManager": "healthy"
  },
  "memory": {
    "used": 123,
    "total": 456,
    "rss": 789
  }
}

GET /monitoring/ready

Kubernetes-style readiness probe - check if application is ready to serve requests.

// Request (no authentication required)
const response = await fetch('https://apiwts.top/monitoring/ready');

// Response 200 - Ready to serve
{
  "status": "ready",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "ready": true,
  "checks": {
    "database": true,
    "cache": true,
    "messageQueue": true
  }
}

// Response 503 - Not ready
{
  "status": "not ready",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "ready": false,
  "checks": {
    "database": true,
    "cache": false,
    "messageQueue": true
  }
}

GET /monitoring/live

Kubernetes-style liveness probe - check if application is alive and not deadlocked.

// Request (no authentication required)
const response = await fetch('https://apiwts.top/monitoring/live');

// Response 200 - Alive
{
  "status": "alive",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "alive": true
}

GET /monitoring/metrics

Application metrics - detailed system and application performance data.

// Request (no authentication required)
const response = await fetch('https://apiwts.top/monitoring/metrics');

// Response 200
{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "system": {
    "uptime": 1186.06,
    "memory": {
      "used": 123,
      "total": 456,
      "rss": 789,
      "external": 12
    },
    "cpu": {
      "user": 5230,
      "system": 1240
    }
  },
  "application": {
    "sessions": {
      "total": 15,
      "active": 12,
      "byState": {
        "READY": 10,
        "INITIALIZING": 2,
        "AUTHENTICATED": 3
      },
      "byTenant": {
        "tenant-001": 8,
        "tenant-002": 7
      }
    },
    "messageQueue": {
      "waiting": 50,
      "active": 5,
      "completed": 1234,
      "failed": 12
    }
  }
}

Admin Diagnostics (Restricted)

✨ NEW in v3.8.13

âš ī¸ Admin Only: This endpoint requires admin authentication and is not intended for public consumption. Access restricted to system administrators.

GET /admin/api/system/diagnostics

Returns system health diagnostics for troubleshooting infrastructure issues.

Responses

  • 200 OK: Returns diagnostics summary with overall system status
  • 401 Unauthorized: Admin authentication required
  • 403 Forbidden: Insufficient permissions
  • 503 Service Unavailable: Diagnostics check failed

📖 Internal Documentation: Detailed schema, response examples, and troubleshooting guide available in docs/progress/v3.8.13-progress.md.

Session Management

POST /api/v1/sessions

Create a new WhatsApp session.

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001'
  })
});

// Response 201
{
  "sessionId": "my-session-001",
  "state": "INITIALIZING",
  "createdAt": "2025-01-15T10:35:00.000Z"
}

GET /api/v1/sessions/:sessionId/qr

Get QR code for WhatsApp authentication.

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001/qr', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200 - Binary image (PNG)
const blob = await response.blob();
const qrUrl = URL.createObjectURL(blob);

GET /api/v1/sessions

List all sessions for your tenant.

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200
{
  "sessions": [
    {
      "sessionId": "my-session-001",
      "state": "READY",
      "phoneNumber": "+5511999999999",
      "createdAt": "2025-01-15T10:35:00.000Z"
    }
  ]
}

DELETE /api/v1/sessions/:sessionId

âš ī¸ PERMANENT DELETION - This action is irreversible!

Permanently delete a WhatsApp session. This will:

  • Remove the session record from the database
  • Delete all session authentication files (requires new QR scan)
  • Cancel any pending scheduled messages for this session
💡 Looking to temporarily disconnect? Use POST /sessions/:sessionId/restart instead - it disconnects the WhatsApp connection but preserves the session for reconnection.
// Request - CAUTION: This permanently deletes the session!
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001', {
  method: 'DELETE',
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 204 - No Content (session permanently deleted)
// Response 404 - Session not found

Session Lifecycle & Operations

Understanding the difference between session operations is crucial:

Operation Endpoint Effect Recoverable?
Create POST /sessions Creates new session record -
Connect POST /sessions/:id/connect Initializes WhatsApp connection Yes
Restart POST /sessions/:id/restart Disconnects + reconnects (keeps session) Yes
Disconnect v3.7.5+ POST /sessions/:id/disconnect Temporarily disconnects (preserves auth files) Yes
Reconnect v3.7.5+ POST /sessions/:id/reconnect Reconnects a disconnected session Yes
Delete DELETE /sessions/:id PERMANENTLY removes session NO
Best Practice: Use /disconnect for temporary disconnections (e.g., "Disconnect WhatsApp" buttons), then /reconnect when ready to reconnect. Use /restart for quick reconnection without state change. Only use DELETE when you truly want to remove the session permanently.

Dangerous Operations IRREVERSIBLE

The following operations are IRREVERSIBLE:

  • DELETE /api/v1/sessions/:sessionId - Permanently deletes session and all authentication files
  • DELETE /api/v1/scheduled-messages/:messageId - Permanently cancels scheduled message

Always implement a confirmation dialog in your UI before calling these endpoints. For temporary disconnection, use POST /sessions/:sessionId/disconnect instead of DELETE.

Example: Temporarily Disconnect WhatsApp

To disconnect a WhatsApp session without deleting it (e.g., for a "Disconnect" button in your UI):

// ✅ CORRECT: Disconnect temporarily (session preserved)
const disconnectWhatsApp = async (sessionId, apiKey) => {
  const response = await fetch(`https://apiwts.top/api/v1/sessions/${sessionId}/disconnect`, {
    method: 'POST',
    headers: { 'X-API-Key': apiKey }
  });

  if (response.ok) {
    const data = await response.json();
    console.log(`Session ${data.sessionId} disconnected`);
    // User can reconnect later using /reconnect endpoint
  }
};

// To reconnect later:
const reconnectWhatsApp = async (sessionId, apiKey) => {
  const response = await fetch(`https://apiwts.top/api/v1/sessions/${sessionId}/reconnect`, {
    method: 'POST',
    headers: { 'X-API-Key': apiKey }
  });
  // Check /qr endpoint if new QR code is needed
};

// ❌ WRONG: This DELETES the session permanently!
// await fetch(`/api/v1/sessions/${sessionId}`, { method: 'DELETE' });

POST /api/v1/sessions/:sessionId/connect

Connect/Initialize a WhatsApp session to start generating QR codes.

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001/connect', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 201 - Connection initiated
{
  "sessionId": "my-session-001",
  "state": "INITIALIZING",
  "message": "Session my-session-001 connection initiated",
  "timestamp": "2025-01-15T10:35:00.000Z"
}

// Response 200 - Already connected
{
  "sessionId": "my-session-001",
  "state": "READY",
  "message": "Session my-session-001 already connected",
  "timestamp": "2025-01-15T10:35:00.000Z"
}

GET /api/v1/sessions/:sessionId/status

Get session status and QR code availability.

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001/status', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200
{
  "sessionId": "my-session-001",
  "state": "READY",
  "qrAvailable": false,
  "lastQrAt": "2025-01-15T10:30:00.000Z",
  "retries": 0,
  "metadata": {}
}

POST /api/v1/sessions/:sessionId/restart

Restart a WhatsApp session (useful for reconnection after errors).

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001/restart', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200
{
  "message": "Session my-session-001 restart initiated",
  "timestamp": "2025-01-15T10:35:00.000Z"
}

GET /api/v1/sessions/:sessionId/health

Get session health and connectivity information.

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001/health', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200
{
  "sessionId": "my-session-001",
  "status": "connected",
  "connected": true,
  "qrAvailable": false,
  "lastSeen": "2025-01-15T10:35:00.000Z",
  "driverType": "wwebjs",
  "metadata": {},
  "timestamp": "2025-01-15T10:35:00.000Z"
}

POST /api/v1/sessions/:sessionId/disconnect v3.7.5+

Temporarily disconnect a WhatsApp session without deleting it. Use this instead of DELETE when you want to disconnect temporarily (e.g., for a "Disconnect" button).

Non-Destructive: This endpoint only disconnects the session. All session data and authentication files are preserved. Use /reconnect to reconnect later.
// Request
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001/disconnect', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200 - Disconnected successfully
{
  "sessionId": "my-session-001",
  "state": "DISCONNECTED",
  "message": "Session has been disconnected. Use /reconnect to reconnect.",
  "timestamp": "2025-01-15T10:35:00.000Z"
}

// Response 200 - Already disconnected (idempotent)
{
  "sessionId": "my-session-001",
  "state": "DISCONNECTED",
  "message": "Session is already disconnected",
  "timestamp": "2025-01-15T10:35:00.000Z"
}

POST /api/v1/sessions/:sessionId/reconnect v3.7.5+

Reconnect a previously disconnected session. If authentication has expired, a new QR code will be generated.

// Request
const response = await fetch('https://apiwts.top/api/v1/sessions/my-session-001/reconnect', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200 - Reconnect initiated
{
  "sessionId": "my-session-001",
  "state": "INITIALIZING",
  "message": "Session reconnect initiated. Check /qr for QR code if needed.",
  "timestamp": "2025-01-15T10:35:00.000Z"
}

Session Reliability & Persistence (v2.87)

✅ Enhanced Session Reliability: Version 2.87 introduces automatic recovery mechanisms and improved session persistence during deployments, ensuring 99%+ uptime for all sessions.

🔧 Detached Frame Auto-Recovery

WhatsApp sessions can occasionally experience Puppeteer/Chromium corruption (detached frame errors) after QR code timeouts or authentication failures. The system now automatically detects and recovers from these errors:

  • Detection: Health checks monitor for "detached Frame" errors every 60 seconds
  • Recovery: Corrupted clients are automatically destroyed and cleaned up
  • State Update: Session marked as DISCONNECTED in database (ready for reconnection)
  • User Action: Click "Generate QR Code" in Admin Dashboard to reconnect

💡 Note: This eliminates the need for manual container restarts when sessions become unresponsive. The system self-heals automatically within 1-2 minutes.

🔄 Blue-Green Session Sync

During zero-downtime deployments, the system now synchronizes session files between the active and target environments:

  • Pre-Deployment Sync: All session authentication files copied from active (blue/green) to target environment
  • Lock Exclusion: Chromium locks excluded (SingletonLock, SingletonSocket) to prevent conflicts
  • Orphan Recovery: Target environment successfully recovers sessions with valid authentication
  • Zero QR Generation: No QR codes required during traffic switch (authentication already valid)

💡 Note: Sessions remain READY (connected) during deployments. No manual intervention required.

â™¨ī¸ Session Warm-up

After session sync, the system performs a 30-second warm-up period to initialize orphan recovery before switching traffic:

  • Health Check Trigger: Validates all sessions in target environment
  • Orphan Recovery: WWebJS detects and reconnects existing session files
  • Smoke Tests: Run AFTER warm-up to validate sessions are READY
  • Traffic Switch: Only proceeds if all validations pass
📊 Expected Uptime

With v2.87 improvements:

  • Before v2.87: 40% uptime during deployments (sessions disconnect, require manual QR scan)
  • After v2.87 (Phase 1): 95%+ uptime (automatic recovery + session sync)
  • Future (Phase 2): 99%+ uptime (shared volume + enhanced lock cleanup)
🚨 Troubleshooting Session Issues

If a session becomes DISCONNECTED:

  1. Check logs: Look for "[v2.87] Detached frame detected" messages
  2. Wait 2 minutes: Auto-recovery runs every 60 seconds (may take up to 2 health check cycles)
  3. Manual reconnection: If still DISCONNECTED, click "Generate QR Code" in Admin Dashboard
  4. Fresh QR scan: Scan QR code with WhatsApp mobile app

💡 Note: If reconnection fails repeatedly, check if your WhatsApp account has been banned or restricted by Meta. Contact support@apiwts.top for assistance.

Messaging

🔒 Idempotency (Duplicate Prevention) - v3.2+ (Enhanced v3.7.9)

To prevent duplicate messages during retries or network failures, ALWAYS include the X-Idempotency-Key header in message sending requests.

Header Format TTL Description
X-Idempotency-Key Alphanumeric, hyphens, underscores (max 255 chars) 24 hours Unique identifier for this request

Behavior:

  • ✅ First request: Message created and queued (HTTP 200)
  • ✅ Retry with SAME key: Returns cached response (HTTP 200, header X-Idempotency-Replay: true)
  • 🔒 Concurrent request with SAME key: Returns HTTP 409 Conflict (v3.7.9+)
  • âš ī¸ Retry with DIFFERENT key: NEW message created (potential duplicate)
  • âš ī¸ Request WITHOUT key: No idempotency protection (each request creates new message)

HTTP 409 Conflict Response (v3.7.9+):

When a request with the same idempotency key is already being processed, the API returns HTTP 409 to prevent race conditions:

// HTTP 409 Conflict
{
  "statusCode": 409,
  "error": "Conflict",
  "message": "Request with idempotency key \"msg-xxx\" is already being processed",
  "retryAfter": 5
}

// Headers:
// Retry-After: 5

Client handling: Wait for retryAfter seconds and retry. The original request will have completed by then.

Recommended format: msg-{uuid-v4} or {client-request-id}

// Example with Idempotency Key (RECOMMENDED)
const response = await fetch('https://apiwts.top/api/v1/messages/text', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json',
    'X-Idempotency-Key': 'msg-550e8400-e29b-41d4-a716-446655440000'  // ← CRITICAL
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    to: '+5511999999999',
    text: 'Hello from WhatsApp API!'
  })
});

// Response (first request)
// HTTP 200
{
  "id": "msg_1234567890",
  "status": "queued"
}

// Response (retry with same key)
// HTTP 200 + Header: X-Idempotency-Replay: true
{
  "id": "msg_1234567890",  // Same ID - no duplicate created
  "status": "queued"
}

POST /api/v1/messages/text

Send text message via WhatsApp.

// Request
const response = await fetch('https://apiwts.top/api/v1/messages/text', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json',
    'X-Idempotency-Key': 'msg-' + crypto.randomUUID()  // ← Recommended
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    to: '+5511999999999',
    text: 'Hello from WhatsApp API!'
  })
});

// Response 200
{
  "id": "msg_1234567890",
  "sessionId": "my-session-001",
  "to": "+5511999999999",
  "status": "queued",
  "timestamp": "2025-01-15T10:35:00.000Z"
}
Field Type Required Description
sessionId string Yes WhatsApp session identifier
to string Yes Recipient phone (E.164 format) or WhatsApp JID (@c.us, @lid, @g.us)
text string Yes Message content (max 4096 characters)
quotedMessageId string No ID of the message to quote/reply to. The sent message will appear as a reply to this message. WWebJS Only
linkPreview boolean No Generate link preview for URLs in the message (default: true)
Replying to Messages (Quoted Messages):

To send a message as a reply to another message, include the quotedMessageId parameter with the serialized ID of the message you want to quote. The recipient will see your message with the quoted message displayed above it.

// Reply to a message
{
  "sessionId": "my-session",
  "to": "5547991246688",
  "text": "Yes, I agree with that!",
  "quotedMessageId": "false_5547991246688@c.us_3EB0A0B0C1D2E3F4"
}

Important: The quotedMessageId must be in the serialized format: {fromMe}_{remote}_{id}

  • fromMe: "true" for messages you sent, "false" for received messages
  • remote: The chat ID (e.g., "5547991246688@c.us")
  • id: The message hash (e.g., "3EB0A0B0C1D2E3F4")

How to get the serializedId: Use the serializedId field from webhook payloads (available since v3.8.18). Example webhook payload:

{
  "event": "message.received.text",
  "message": {
    "id": "3EB0A0B0C1D2E3F4",
    "serializedId": "false_5547991246688@c.us_3EB0A0B0C1D2E3F4"  // Use this for quotedMessageId
  }
}

Note: The message being quoted must be in WhatsApp Web's cache (approximately the last 500 messages in the conversation).

Graceful Degradation (v3.8.20+): If the quoted message is not found in the cache, the API will log a warning and send the message without the quote (the message will still be delivered, just not as a reply). This ensures message delivery is never blocked by cache limitations.

POST /api/v1/messages/media

Send media messages (images, videos, audio, documents).

// Send image by URL
const response = await fetch('https://apiwts.top/api/v1/messages/media', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    to: '+5511999999999',
    mediaUrl: 'https://example.com/image.jpg',
    mimetype: 'image/jpeg',
    caption: 'Check out this image!'
  })
});

// Send document
const response = await fetch('https://apiwts.top/api/v1/messages/media', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    to: '+5511999999999',
    mediaUrl: 'https://example.com/document.pdf',
    mimetype: 'application/pdf',
    filename: 'invoice.pdf',
    caption: 'Your invoice is attached'
  })
});
Field Type Required Description
sessionId string ✅ WhatsApp session identifier
to string ✅ Recipient phone (international format)
mediaUrl string * URL to media file
mediaBase64 string * Base64 encoded media
mimetype string ✅ MIME type (image/jpeg, video/mp4, etc.)
caption string ❌ Media caption (max 1024 chars)
filename string ❌ Custom filename for documents

Supported Media Types:

  • Images: JPEG, PNG, WebP (max 16MB)
  • Videos: MP4, AVI, MOV (max 16MB)
  • Audio: MP3, WAV, AAC, OGG (max 16MB)
  • Documents: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT (max 100MB)

GET /api/v1/messages/:messageId

Get the status of a sent message.

// Request
const response = await fetch('https://apiwts.top/api/v1/messages/msg_abc123', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200
{
  "id": "msg_abc123",
  "sessionId": "my-session-001",
  "to": "+5511999999999",
  "status": "sent",
  "type": "text",
  "timestamp": "2025-01-15T10:35:00.000Z",
  "driverMessageId": "3EB0XXXXXXXXXXXXX"
}

GET /api/v1/messages

List all messages for the authenticated tenant with pagination.

// Request with filters
const response = await fetch('https://apiwts.top/api/v1/messages?page=1&limit=20&sessionId=my-session-001&status=sent', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200
{
  "messages": [
    {
      "id": "msg_abc123",
      "sessionId": "my-session-001",
      "to": "+5511999999999",
      "status": "sent",
      "type": "text",
      "timestamp": "2025-01-15T10:35:00.000Z",
      "driverMessageId": "3EB0XXXXXXXXXXXXX"
    }
  ],
  "total": 150,
  "page": 1,
  "limit": 20
}
Query Parameter Type Default Description
page number 1 Page number (min: 1)
limit number 20 Messages per page (min: 1, max: 100)
sessionId string - Filter by session ID (optional)
status string - Filter by status: queued, sent, delivered, read, failed (optional)

⏰ Scheduled Messages (v2.46)

Schedule WhatsApp messages to be sent at a future date and time. No maximum scheduling limit - you can schedule messages years in advance for use cases like insurance renewals, warranty expirations, annual contracts, etc.

🎉 v2.46 Updates:
  • ✅ Real-time Stats API: Get pending/sent/failed/total counters via /messages/scheduled/stats
  • ✅ Advanced Filters: Filter by tenant, status, date range via query parameters
  • ✅ Enhanced Security: HTML sanitization on all user-generated content
✨ v2.55 Updates (2025-10-10):
  • ✅ Enhanced Buffer: Increased from 1min to 2min (120s) to account for network latency + clock skew
  • ✅ Improved Error Messages: Detailed timing context with actionable suggestions
  • ✅ Better UX: Errors now include exact timestamps, buffer requirements, and suggested valid time

POST /api/v1/messages/scheduled

Schedule a message for future delivery.

// Schedule message 1 year from now (insurance renewal)
const response = await fetch('https://apiwts.top/api/v1/messages/scheduled', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    to: '+5511999999999',
    text: 'Your car insurance will expire in 30 days. Renew now to avoid coverage gaps!',
    scheduledAt: '2026-01-15T10:00:00Z'
  })
});

// Response
{
  "id": "msg_abc123",
  "sessionId": "my-session-001",
  "to": "+5511999999999",
  "text": "Your car insurance will expire in 30 days...",
  "scheduledAt": "2026-01-15T10:00:00.000Z",
  "status": "pending",
  "createdAt": "2025-01-15T10:00:00.000Z"
}

Error Response (400 Bad Request - Scheduling Too Soon)

v2.55: Enhanced error messages with detailed timing context and actionable suggestions.

// Attempt to schedule message less than 2 minutes in the future
{
  "statusCode": 400,
  "error": "SchedulingTooSoon",
  "message": "Message scheduled too close to current time",
  "details": {
    "scheduledAt": "2025-10-10T10:01:00Z",        // Your requested time
    "currentTime": "2025-10-10T10:00:00Z",         // Server current time
    "minimumScheduleTime": "2025-10-10T10:02:00Z", // Minimum valid time
    "minimumBufferSeconds": 120,                    // Required buffer (2 minutes)
    "timeUntilScheduled": "60s",                    // How early you are
    "suggestion": "Schedule at least 120s in the future. Try: 2025-10-10T10:02:00Z"
  }
}

// How to fix: Use the suggested timestamp from error.details.minimumScheduleTime
const errorResponse = await response.json();
if (errorResponse.error === 'SchedulingTooSoon') {
  // Retry with suggested time
  const suggestedTime = errorResponse.details.minimumScheduleTime;
  console.log(`Retrying with suggested time: ${suggestedTime}`);
}
Field Type Required Description
sessionId string ✅ WhatsApp session identifier
to string ✅ Recipient phone (E.164 format: +5511999999999)
text string ✅ Message content (1-4096 characters)
scheduledAt string (ISO 8601) ✅ Future timestamp (min: +2 minutes, no max limit)
⏰ Scheduling Rules (v2.55):
  • Minimum: 2 minutes from now (120 seconds)
  • Maximum: No limit (can schedule years ahead)
  • Processing: Checked automatically every minute
  • Session Validation: Relaxed - can schedule even if session disconnected (will auto-retry when reconnected)
  • Buffer Reason: Accounts for network latency, clock skew, and processing time

GET /api/v1/messages/scheduled

List scheduled messages with optional filters.

// List all pending scheduled messages for a session
const response = await fetch('https://apiwts.top/api/v1/messages/scheduled?sessionId=my-session-001&status=pending', {
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "messages": [
    {
      "id": "msg_abc123",
      "sessionId": "my-session-001",
      "to": "+5511999999999",
      "text": "Insurance renewal reminder",
      "scheduledAt": "2026-01-15T10:00:00.000Z",
      "status": "pending",
      "createdAt": "2025-01-15T10:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 50
}
Query Parameter Type Description
sessionId string Filter by session ID
status string Filter by status (pending, queued, sent, failed)
startDate ISO 8601 Filter messages scheduled after this date
endDate ISO 8601 Filter messages scheduled before this date
page number Page number (default: 1)
limit number Results per page (default: 50)

DELETE /api/v1/messages/scheduled/:id

Cancel a scheduled message (only if status is pending).

// Cancel scheduled message
const response = await fetch('https://apiwts.top/api/v1/messages/scheduled/msg_abc123', {
  method: 'DELETE',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response (200 OK)
{
  "success": true,
  "message": "Scheduled message cancelled successfully"
}

// Response (404 Not Found - if already sent or not pending)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Message not found or cannot be cancelled (already sent or not scheduled)"
}

DELETE /api/v1/messages/:messageId

v2.84+ Revoke/delete a sent message. Supports "delete for me" or "delete for everyone" (within ~48 hours).

Important: Only supported on wwebjs driver. Cloud API driver will return 422 error.

Query Parameters

Parameter Type Required Default Description
type string No everyone Revoke type: me (delete for yourself only) or everyone (delete for all participants)

Message Status Requirements

Only messages with status sent, delivered, or read can be revoked. Messages with status pending, queued, failed, or revoked will return 400 error.

Time Constraint

WhatsApp allows deleting messages for everyone only within approximately 48 hours of sending. After this period, you can only delete for yourself (type=me).

// Delete message for everyone (default)
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000', {
  method: 'DELETE',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Delete message for yourself only
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000?type=me', {
  method: 'DELETE',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response (200 OK - Success)
{
  "success": true,
  "message": "Message revoked successfully (everyone)",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "status": "revoked",
    "revokedAt": "2025-11-23T06:00:00.000Z",
    "revokeType": "everyone",
    "revokeReason": "User-initiated via API (DELETE /messages/...?type=everyone)"
  }
}

// Response (200 OK - Already Revoked - Idempotent)
{
  "success": true,
  "message": "Message already revoked",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "status": "revoked",
    "revokedAt": "2025-11-23T05:55:00.000Z",
    "revokeType": "everyone"
  }
}

// Response (400 Bad Request - Invalid Status)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot revoke message with status 'pending'. Only 'sent', 'delivered', or 'read' messages can be revoked."
}

// Response (403 Forbidden - Unauthorized)
{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "You are not authorized to revoke this message"
}

// Response (404 Not Found)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Message not found"
}

// Response (410 Gone - Time Expired)
{
  "statusCode": 410,
  "error": "Gone",
  "message": "Message revocation time limit expired (can only delete for yourself now)"
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Message revocation is not supported by this driver. Only WWebJS driver supports deleting sent messages.",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}

POST /api/v1/messages/:messageId/react

v2.85+ React to a message with an emoji (WWebJS only).

Important: Only supported on wwebjs driver. Cloud API driver will return 422 error.

Request Body

Parameter Type Required Description
emoji string Yes Any valid Unicode emoji to add reaction, or empty string "" to remove existing reaction (v3.8.32+). Common examples: 👍 👎 â¤ī¸ 😂 😮 đŸ˜ĸ 🙏 đŸ”Ĩ ✅ ❌ 🎉 đŸ’¯.
// React with thumbs up emoji
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/react', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    emoji: '👍'
  })
});

// Response (200 OK - Success)
{
  "success": true,
  "message": "Reaction \"👍\" added successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "emoji": "👍",
    "reactedAt": "2025-11-23T08:00:00.000Z"
  }
}

// Remove reaction (v3.8.32+) - Send empty string
body: JSON.stringify({
  emoji: ''  // Empty string removes existing reaction
})

// Response (200 OK - Reaction Removed)
{
  "success": true,
  "message": "Reaction removed successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "emoji": "",
    "reactedAt": "2026-01-05T10:30:00.000Z"
  }
}

// Response (400 Bad Request - Invalid Status)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot react to message with status 'queued'. Only 'sent', 'delivered', or 'read' messages can receive reactions."
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Message reactions are not supported by this driver",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}

POST /api/v1/sessions/:sessionId/messages/:driverMessageId/react

v3.8.17+ WWebJS Only

Add an emoji reaction to a received (inbound) message using the driverMessageId from the webhook payload.

When to Use Each Endpoint:
  • POST /messages/:messageId/react - For sent messages (you have the UUID from the database)
  • POST /sessions/:sessionId/messages/:driverMessageId/react - For received messages (you have the driverMessageId from webhook)

Request Parameters

ParameterTypeRequiredDescription
sessionIdstringYesSession ID (path parameter)
driverMessageIdstringYesDriver message ID from webhook payload (path parameter)
emojistringYesEmoji to react with, or empty string "" to remove existing reaction (v3.8.32+)

Finding the driverMessageId

The webhook payload contains two ID formats. Use serializedId for the /react endpoint:

ID Formats (v3.8.18+):
  • id - Short format (e.g., "3EB0ABC123DEF456789") - for display/logging only
  • serializedId - Full format (e.g., "false_55...@c.us_3EB0...") - required for /react endpoint
// Webhook payload example (message.received.text) - v3.8.18+
{
  "event": "message.received.text",
  "sessionId": "mysession",
  "message": {
    "id": "3EB0ABC123DEF456789",  // Short format (display only)
    "serializedId": "false_5547991246688@c.us_3EB0ABC123DEF456789",  // <-- USE THIS for /react
    "from": "5547991246688@c.us",
    "to": "5511999887766@c.us",
    "body": "Hello!",
    "timestamp": "2026-01-02T10:30:00.000Z"
  }
}
// React to an inbound message (use serializedId from webhook)
const response = await fetch('https://apiwts.top/api/v1/sessions/mysession/messages/false_5547991246688@c.us_3EB0ABC123DEF456789/react', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key'
  },
  body: JSON.stringify({
    emoji: "thumbs up"
  })
});

// Response (200 OK)
{
  "success": true,
  "message": "Reaction \"thumbs up\" added successfully",
  "data": {
    "driverMessageId": "false_5547991246688@c.us_3EB0ABC123DEF456789",
    "emoji": "thumbs up",
    "reactedAt": "2026-01-02T10:31:00.000Z"
  }
}

// Response (404 Not Found - Session)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session mysession not found",
  "code": "SESSION_NOT_FOUND"
}

// Response (400 Bad Request - Session Not Active)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Session mysession is not active",
  "code": "SESSION_NOT_ACTIVE"
}

// Response (404 Not Found - Message Not in Cache)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Message not found. It may have been deleted or is no longer in the WhatsApp cache (~500 recent messages per chat).",
  "code": "MESSAGE_NOT_FOUND"
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Message reactions are not supported by this driver",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}
Cache Limitation: The message must be in the WhatsApp Web cache (approximately the last 500 messages per chat). Older messages may return a 404 error even if the driverMessageId is correct.

GET /api/v1/messages/:messageId/info

v2.85+ Get detailed delivery information about a message, including ACK level and group delivery details (WWebJS only).

Important: Only supported on wwebjs driver. Cloud API driver will return 422 error.

ACK Levels

  • -1: Error (message failed to send)
  • 0: Pending (message queued on client)
  • 1: Server (message sent to WhatsApp server)
  • 2: Device (message delivered to recipient device)
  • 3: Read (message read by recipient)
  • 4: Played (voice/video message played by recipient)
// Get message delivery info
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/info', {
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response (200 OK - Individual Chat)
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "ack": 3,
    "timestamp": "2025-11-23T08:00:00.000Z"
  }
}

// Response (200 OK - Group Chat with Delivery Details)
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "ack": 3,
    "timestamp": "2025-11-23T08:00:00.000Z",
    "deliveryDetails": {
      "deliveredTo": [
        {
          "contact": "5511999999999@c.us",
          "timestamp": "2025-11-23T08:00:05.000Z"
        }
      ],
      "readBy": [
        {
          "contact": "5511999999999@c.us",
          "timestamp": "2025-11-23T08:01:00.000Z"
        }
      ]
    }
  }
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Message delivery info is not supported by this driver",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}

POST /api/v1/messages/:messageId/forward

v2.85+ Forward a message to another chat (WWebJS only).

Important: Only supported on wwebjs driver. Cloud API driver will return 422 error.

Request Body

Parameter Type Required Description
targetChatId string Yes WhatsApp chat ID to forward to (e.g., "5511999999999@c.us")
// Forward message to another chat
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/forward', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    targetChatId: '5511888888888@c.us'
  })
});

// Response (200 OK - Success)
{
  "success": true,
  "message": "Message forwarded successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "targetChatId": "5511888888888@c.us",
    "forwardedAt": "2025-11-23T08:00:00.000Z"
  }
}

// Response (400 Bad Request - Invalid Status)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot forward message with status 'failed'. Only 'sent', 'delivered', or 'read' messages can be forwarded."
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Message forwarding is not supported by this driver",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}

PATCH /api/v1/messages/:messageId

v3.4+ Edit a sent text message (WWebJS only).

Important: Only supported on wwebjs driver. Cloud API driver will return 422 error. WhatsApp allows editing messages within ~15 minutes of sending.

Request Body

Parameter Type Required Description
newContent string Yes New text content for the message (max 4096 characters)
// Edit a sent text message
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000', {
  method: 'PATCH',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    newContent: 'Updated message content'
  })
});

// Response (200 OK - Success)
{
  "success": true,
  "message": "Message edited successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "newContent": "Updated message content",
    "editedAt": "2025-11-28T10:00:00.000Z"
  }
}

// Response (400 Bad Request - Invalid Message Type)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot edit message of type 'media'. Only text messages can be edited.",
  "code": "INVALID_MESSAGE_TYPE"
}

// Response (400 Bad Request - Invalid Status)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot edit message with status 'failed'. Only sent/delivered/read messages can be edited.",
  "code": "INVALID_MESSAGE_STATE"
}

// Response (410 Gone - Edit Window Expired)
{
  "statusCode": 410,
  "error": "Gone",
  "message": "Message edit window has expired. Messages can only be edited within 15 minutes of sending.",
  "code": "MESSAGE_EDIT_WINDOW_EXPIRED"
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Message editing is not supported by this driver",
  "code": "MESSAGE_EDIT_NOT_SUPPORTED",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}

GET /api/v1/messages/:messageId/media

v3.4+ v3.7.12+ Enhanced WWebJS Only

Download media from a message (WWebJS only).

v3.7.12+ Webhook Integration

Starting in v3.7.12, message.received.media webhooks include a mediaUrl field that points directly to this endpoint. The URL uses a driverMessageId format that is cached for a configurable TTL (default: 5 minutes).

Flow:

  1. Receive message.received.media webhook with mediaUrl
  2. Call GET on the mediaUrl within TTL window
  3. Receive media as binary response (Content-Type: image/jpeg, audio/ogg, etc.)

TTL Configuration (v3.7.12+)

Media references are cached with configurable TTL:

  • MEDIA_DOWNLOAD_TTL_SECONDS env var (default: 300 = 5 minutes)
  • Range: 60-1800 seconds (1-30 minutes)
  • After TTL expires, returns 404 "Media reference expired"

Supported messageId Formats

  • UUID: 550e8400-e29b-41d4-a716-446655440000 (database lookup)
  • driverMessageId: AC99FE4B684B8EE1AF5A6940ED188444 (cache lookup from webhook)

Important: Only supported on wwebjs driver. Cloud API provides media URLs directly in webhooks with pre-signed URLs instead.

Authentication: Both X-API-Key: sk_live_... and Authorization: Bearer sk_live_... headers are supported. Use whichever matches your application's standard pattern.

// v3.7.12+: Download media using mediaUrl from webhook
// Webhook payload includes: "mediaUrl": "https://apiwts.top/api/v1/messages/AC99FE4B.../media"
const response = await fetch(webhook.message.mediaUrl, {
  headers: {
    'X-API-Key': 'sk_live_your_api_key'
    // OR: 'Authorization': 'Bearer sk_live_your_api_key'
  }
});

// Response (200 OK - Binary media data)
// Headers:
//   Content-Type: image/jpeg (or audio/ogg, video/mp4, application/pdf, etc.)
//   Content-Disposition: attachment; filename="photo.jpg"
//   Content-Length: 245678
// Body: Binary media data

// Alternative: Download using database UUID (original method)
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/media', {
  headers: {
    'X-API-Key': 'sk_live_your_api_key'
    // OR: 'Authorization': 'Bearer sk_live_your_api_key'
  }
});

// Response (200 OK - JSON with base64)
{
  "success": true,
  "message": "Media downloaded successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "base64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQ...",
    "mimetype": "image/jpeg",
    "filename": "photo.jpg",
    "filesize": 245678
  }
}

// Response (400 Bad Request - No Media)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Message does not contain media",
  "code": "MESSAGE_HAS_NO_MEDIA"
}

// Response (404 Not Found - Message not in database)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Message 550e8400-e29b-41d4-a716-446655440000 not found",
  "code": "MESSAGE_NOT_FOUND"
}

// Response (404 Not Found - v3.7.12+ TTL Expired)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Media reference expired or not found. The download link has a limited TTL.",
  "code": "MEDIA_REFERENCE_EXPIRED"
}

// Response (403 Forbidden - Tenant mismatch)
{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "You do not have permission to access this media",
  "code": "FORBIDDEN"
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Media download is not supported by this driver. Only WWebJS driver supports downloading media from messages. Cloud API provides media URLs directly in webhooks.",
  "code": "MEDIA_DOWNLOAD_NOT_SUPPORTED",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}

POST /api/v1/messages/location

v3.4+ Send a location message with geographic coordinates.

Supported drivers: wwebjs, cloud-api

// Send location message
const response = await fetch('https://apiwts.top/api/v1/messages/location', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    to: '5511999999999',
    latitude: -23.5505199,
    longitude: -46.6333094,
    description: 'SÃŖo Paulo, Brazil',
    address: 'Av. Paulista, 1578 - Bela Vista'
  })
});

// Response (200 OK - Success)
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "driverMessageId": "true_5511999999999@c.us_ABCDEF123456",
  "sessionId": "my-session-001",
  "to": "5511999999999",
  "latitude": -23.5505199,
  "longitude": -46.6333094,
  "description": "SÃŖo Paulo, Brazil",
  "address": "Av. Paulista, 1578 - Bela Vista",
  "status": "sent",
  "timestamp": "2025-11-28T14:00:00.000Z"
}

// Response (400 Bad Request - Invalid Coordinates)
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Latitude must be between -90 and 90"
}

// Response (404 Not Found - Session Not Found)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session my-session-001 not found"
}

// Response (422 Unprocessable Entity - Driver Not Supported)
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Location messages are not supported by this driver",
  "code": "LOCATION_SEND_NOT_SUPPORTED",
  "driver": "cloud-api",
  "supportedDrivers": ["wwebjs"]
}

Request Parameters

Parameter Type Required Description
sessionId string Yes WhatsApp session ID
to string Yes Recipient phone number or WhatsApp JID
latitude number Yes Latitude (-90 to 90)
longitude number Yes Longitude (-180 to 180)
description string No Location name/description (max 256 chars)
address string No Address text (max 256 chars)

GET /api/v1/messages/:messageId/quoted

v3.4+ WWebJS Only

Get the original message that was quoted/replied to in a message. Returns the quoted message content or null if the message is not a reply. Useful for building conversation threads.

// Get quoted message from a reply
const response = await fetch('https://apiwts.top/api/v1/messages/msg-uuid-here/quoted', {
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response (when message is a reply)
{
  "messageId": "msg-uuid-here",
  "quotedMessage": {
    "id": "true_5511999999999@c.us_AAABB...",
    "from": "5511888888888@c.us",
    "to": "5511999999999@c.us",
    "body": "Original message text",
    "type": "chat",
    "timestamp": "2024-01-15T10:30:00.000Z",
    "hasMedia": false
  }
}

// Response (when message is NOT a reply)
{
  "messageId": "msg-uuid-here",
  "quotedMessage": null
}

Response Fields

Field Type Description
quotedMessage.id string WhatsApp message ID of the quoted message
quotedMessage.from string Sender of the quoted message (WhatsApp JID)
quotedMessage.to string Recipient of the quoted message (WhatsApp JID)
quotedMessage.body string Text content of the quoted message
quotedMessage.type string Message type (chat, image, video, etc.)
quotedMessage.timestamp string ISO 8601 timestamp when the quoted message was sent
quotedMessage.hasMedia boolean Whether the quoted message contains media

POST /api/v1/messages/:messageId/star

v3.4+ WWebJS Only

Star (favorite) a message. Starred messages appear in the "Starred Messages" section of the chat.

// Star a message
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/star', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "success": true,
  "message": "Message starred successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "starredAt": "2024-01-15T10:30:00.000Z"
  }
}

DELETE /api/v1/messages/:messageId/star

v3.4+ WWebJS Only

Remove star (unfavorite) from a message.

// Unstar a message
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/star', {
  method: 'DELETE',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "success": true,
  "message": "Message unstarred successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "unstarredAt": "2024-01-15T10:30:00.000Z"
  }
}

GET /api/v1/messages/:messageId/reactions

v3.4+ WWebJS Only

Get all emoji reactions on a message. Returns list of reactions with emoji, sender ID, and timestamp. Useful for analyzing engagement and user feedback.

// Get reactions on a message
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/reactions', {
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "messageId": "550e8400-e29b-41d4-a716-446655440000",
  "reactions": [
    {
      "emoji": "👍",
      "senderId": "5511999999999@c.us",
      "timestamp": "2024-01-15T10:30:00.000Z"
    },
    {
      "emoji": "â¤ī¸",
      "senderId": "5511888888888@c.us",
      "timestamp": "2024-01-15T10:31:00.000Z"
    }
  ],
  "totalCount": 2
}

POST /api/v1/messages/:messageId/pin

v3.4+ WWebJS Only

Pin a message in chat for a specified duration. The message will be pinned at the top of the chat for the specified time. Useful for highlighting important announcements or information in groups.

// Pin a message for 1 day (86400 seconds)
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/pin', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    durationSeconds: 86400  // 1 day (min: 60s, max: 2592000s = 30 days)
  })
});

// Response
{
  "success": true,
  "message": "Message pinned successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "pinned": true,
    "durationSeconds": 86400
  }
}

DELETE /api/v1/messages/:messageId/pin

v3.4+ WWebJS Only

Unpin a pinned message in chat. Removes the message from the pinned position in the chat.

// Unpin a message
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/pin', {
  method: 'DELETE',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "success": true,
  "message": "Message unpinned successfully",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "pinned": false
  }
}

GET /api/v1/messages/:messageId/mentions

v3.4+ WWebJS Only

Get all @mentions in a message. Returns list of mentioned contacts with their IDs, names (if available), phone numbers, and whether they are groups. Useful for processing messages that mention users or groups.

// Get mentions in a message
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/mentions', {
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "messageId": "550e8400-e29b-41d4-a716-446655440000",
  "mentions": [
    {
      "id": "5511999999999@c.us",
      "name": "John Doe",
      "phoneNumber": "5511999999999",
      "isGroup": false
    },
    {
      "id": "5511888888888@c.us",
      "phoneNumber": "5511888888888",
      "isGroup": false
    }
  ],
  "totalCount": 2
}

GET /api/v1/messages/:messageId/poll-votes

v3.4+ WWebJS Only

Get all votes from a poll message. Returns list of voters with their selected options and vote timestamps. Only works on poll_creation type messages.

// Get votes from a poll message
const response = await fetch('https://apiwts.top/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/poll-votes', {
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "messageId": "550e8400-e29b-41d4-a716-446655440000",
  "votes": [
    {
      "voter": "5511999999999@c.us",
      "selectedOptions": ["Option A", "Option C"],
      "interactedAt": "2025-01-15T10:30:00.000Z"
    },
    {
      "voter": "5511888888888@c.us",
      "selectedOptions": ["Option B"],
      "interactedAt": "2025-01-15T10:35:00.000Z"
    }
  ],
  "totalVoters": 2
}

GET /api/v1/messages/scheduled/stats

Get count of scheduled messages by status.

// Get scheduled messages statistics
const response = await fetch('https://apiwts.top/api/v1/messages/scheduled/stats', {
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response
{
  "pending": 45,
  "queued": 3,
  "sent": 1200,
  "failed": 8
}

Status Lifecycle

  • pending: Scheduled, waiting for execution time
  • queued: Execution time reached, enqueued in send-message queue
  • sent: Successfully sent via WhatsApp driver
  • failed: Failed to send (session deleted, network error, etc.)

Use Cases

  • Short-term: Appointment reminders (24h), follow-ups (3 days), marketing campaigns (1 week)
  • Long-term: Insurance renewals (1 year), warranty expirations (11 months), annual contracts
  • Business: Birthday messages, subscription renewals, seasonal campaigns
âš ī¸ Important Notes:
  • All rate limiting and retry logic applies to scheduled messages
  • If session is disconnected at send time, message will be retried automatically when session reconnects
  • If session is deleted before send time, message will be marked as failed
  • Messages are checked every 1 minute - scheduling accuracy is Âą60 seconds

🔔 Webhooks - Real-time Notifications

Configure webhooks to receive real-time notifications when messages arrive, status updates occur, and other events happen.

POST /api/v1/webhooks/configure

Set up webhook URL and events.

// Request
const response = await fetch('https://apiwts.top/api/v1/webhooks/configure', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://your-app.com/webhooks/whatsapp',
    events: [
      'message.received.text',
      'message.received.media',
      'message.sent',
      'message.delivered',
      'message.read'
    ],
    secret: 'your-webhook-secret'
  })
});

Available Events

  • message.received.text - Text message received
  • message.received.media - Media message received
  • message.received.location - Location message received
  • message.sent - Message successfully sent
  • message.delivered - Message delivered to recipient
  • message.read - Message read by recipient (blue ticks)
  • message.status_update - Message status changed (v2.81+, real-time delivery tracking) ✨
  • session.ready - WhatsApp session ready
  • session.disconnected - WhatsApp session disconnected
✨ NEW in v2.81: Real-Time Message Status Tracking

v2.81+: The API now automatically tracks message delivery status via WhatsApp ACK (acknowledgment) events. Status updates happen in real-time without requiring API polling:

  • pending → Message queued, waiting to be sent (gray clock icon)
  • sent → Message reached WhatsApp servers (single gray checkmark)
  • delivered → Message reached recipient's device (double gray checkmarks)
  • read → Message opened by recipient (double blue checkmarks)
  • failed → Message delivery failed (red exclamation)

How it works: WWebJS driver listens for message_ack events from WhatsApp and automatically updates the database. If you have webhooks configured for message.status_update, you'll receive real-time notifications whenever status changes occur.

Webhook Payload Example

v2.70.2+: Payload fields are now flattened to top-level for easier consumption. The full nested data is still available in the data field for backward compatibility.

v3.0.3+: Added fromType field to indicate identifier format. See Identifier Formats section below.

v3.4+: Added 11 contextual fields for richer webhook payloads:

  • isForwarded, forwardingScore - Forwarded message info (score max 127)
  • links - URLs extracted from message body
  • author - Sender in group messages
  • isEphemeral - Disappearing message flag
  • duration - Voice/video message duration in seconds
  • vCards - Contact cards array for contact messages
  • isStarred, isGif, isStatus, hasReaction, broadcast - Message flags
  • deviceType - Device that sent the message (web, android, iphone, etc.)

v3.8.21+: Added hasQuotedMsg and quotedMsg fields for quote/reply support.

v3.8.25+: Added quotedMsgId field + improved reliability with fallback mechanism.

  • hasQuotedMsg - Boolean indicating if message is a reply to another message
  • quotedMsgId - v3.8.25+ Direct access to quoted message serialized ID (null if not a reply)
  • quotedMsg - Object with quoted message data (null if not a reply or data unavailable):
    • id - Serialized ID of the quoted message
    • body - Text content of the quoted message (may be null if unavailable)
    • from - Sender JID of the quoted message (may be null if unavailable)
    • type - Message type (chat, image, video, etc.)
    • timestamp - ISO 8601 timestamp of the quoted message (may be null if unavailable)
// Text message received (v3.4+ format with all contextual fields)
{
  "event": "message.received.text",
  "timestamp": "2025-01-15T10:35:23.000Z",
  "sessionId": "my-session-001",
  "message": {
    "id": "msg_987654321",
    "from": "5511999999999@c.us",           // WhatsApp JID format (see Identifier Formats)
    "fromType": "phone",                     // v3.0.3+: 'phone' | 'lid' | 'group' | 'unknown'
    "to": "554791246688@c.us",
    "timestamp": "2025-01-15T10:35:23.000Z",
    "type": "chat",
    "body": "Check out this link: https://example.com",
    "hasMedia": false,
    "isForwarded": false,                    // v3.4+: Whether message was forwarded
    "forwardingScore": 0,                    // v3.4+: How many times forwarded (0-127)
    "links": ["https://example.com"],        // v3.4+: URLs extracted from message (optional)
    "isGroup": false,                        // Whether message is from a group
    "isEphemeral": false,                    // v3.4+: Disappearing message
    "isStarred": false,                      // v3.4+: Whether message is starred
    "isGif": false,                          // v3.4+: Whether message is a GIF
    "isStatus": false,                       // v3.4+: Whether message is a status update
    "hasReaction": false,                    // v3.4+: Whether message has reactions
    "hasQuotedMsg": false,                   // v3.8.21+: Whether message is a reply
    "broadcast": false,                      // v3.4+: Whether message was broadcast
    "deviceType": "android",                 // v3.4+: Device type (web, android, iphone, etc.)
    "metadata": {                            // Additional metadata from driver
      "chatId": "5511999999999@c.us",
      "chatName": "John Doe",                // Contact/chat name
      "contactName": "John Doe",             // Contact name from address book
      "hasMedia": false
    }
  }
}

// Message from Linked Identity (Switzerland, Business accounts, etc.)
// âš ī¸ IMPORTANT: @lid identifiers are NOT phone numbers!
{
  "event": "message.received.text",
  "timestamp": "2025-01-15T10:35:23.000Z",
  "sessionId": "my-session-001",
  "message": {
    "id": "msg_lid_example",
    "from": "77442588909785@lid",             // Linked Identity (NOT a phone number!)
    "fromType": "lid",                        // v3.0.3+: Use metadata.chatName for contact name
    "to": "554791246688@c.us",
    "timestamp": "2025-01-15T10:35:23.000Z",
    "type": "chat",
    "body": "Hello from Switzerland!",
    "hasMedia": false,
    "metadata": {
      "chatId": "77442588909785@lid",
      "chatName": "Giovanni Moser",          // ← Use this for contact identification with @lid
      "contactName": "Giovanni Moser",
      "hasMedia": false
    }
  }
}

// Media message received (v3.7.12+: includes mediaUrl for download)
{
  "event": "message.received.media",
  "timestamp": "2025-01-15T10:36:55.000Z",
  "sessionId": "my-session-001",
  "message": {
    "id": "msg_image_12345",
    "from": "5511999999999@c.us",
    "fromType": "phone",
    "to": "554791246688@c.us",
    "timestamp": "2025-01-15T10:36:55.000Z",
    "type": "image",
    "body": "Check this out!",
    "hasMedia": true,
    "mediaUrl": "https://apiwts.top/api/v1/messages/AC99FE4B684B8EE1AF5A6940ED188444/media",
    "metadata": {
      "chatName": "John Doe",
      "hasMedia": true
    }
  }
}

// Group message received (v3.4+ with author and isForwarded)
{
  "event": "message.received.text",
  "timestamp": "2025-01-15T10:37:00.000Z",
  "sessionId": "my-session-001",
  "message": {
    "id": "msg_group_12345",
    "from": "120363220421466993@g.us",        // Group JID
    "fromType": "group",                      // v3.0.3+: This is a group message
    "to": "554791246688@c.us",
    "timestamp": "2025-01-15T10:37:00.000Z",
    "type": "chat",
    "body": "Hello team!",
    "hasMedia": false,
    "isGroup": true,                          // v3.4+: Message is from a group
    "author": "5511999999999@c.us",           // v3.4+: Actual sender within the group
    "isForwarded": true,                      // v3.4+: This message was forwarded
    "forwardingScore": 3,                     // v3.4+: Forwarded 3 times
    "metadata": {
      "chatId": "120363220421466993@g.us",
      "chatName": "Project Team",             // Group name
      "hasMedia": false
    }
  }
}

// Reply/quoted message received (v3.8.21+ with hasQuotedMsg and quotedMsg, v3.8.25+ with quotedMsgId)
{
  "event": "message.received.text",
  "timestamp": "2026-01-02T11:00:00.000Z",
  "sessionId": "my-session-001",
  "message": {
    "id": "msg_reply_12345",
    "serializedId": "false_5547991246688@c.us_3EB0ABC123",
    "from": "5547991246688@c.us",
    "fromType": "phone",
    "to": "554791246688@c.us",
    "timestamp": "2026-01-02T11:00:00.000Z",
    "type": "chat",
    "body": "Yes, I received it!",            // The reply message text
    "hasMedia": false,
    "hasQuotedMsg": true,                     // v3.8.21+: This is a reply to another message
    "quotedMsgId": "false_554791246688@c.us_3EB0DEF456",  // v3.8.25+: Direct access to quoted ID
    "quotedMsg": {                            // v3.8.21+: The original message being quoted
      "id": "false_554791246688@c.us_3EB0DEF456",
      "body": "Did you receive the document?",
      "from": "554791246688@c.us",
      "type": "chat",
      "timestamp": "2026-01-02T10:55:00.000Z"
    },
    "metadata": {
      "chatId": "5547991246688@c.us",
      "chatName": "John Doe",
      "contactName": "John Doe",
      "hasMedia": false
    }
  }
}

// Voice message received (v3.4+ with duration, v3.7.12+ with mediaUrl)
{
  "event": "message.received.media",
  "timestamp": "2025-01-15T10:38:00.000Z",
  "sessionId": "my-session-001",
  "message": {
    "id": "msg_voice_12345",
    "from": "5511999999999@c.us",
    "fromType": "phone",
    "to": "554791246688@c.us",
    "timestamp": "2025-01-15T10:38:00.000Z",
    "type": "ptt",                            // ptt = push-to-talk (voice message)
    "hasMedia": true,
    "mediaUrl": "https://apiwts.top/api/v1/messages/B7C8D9E0F1234567890123456789ABCD/media",
    "duration": 15,                           // v3.4+: Duration in seconds
    "deviceType": "iphone",                   // v3.4+: Sent from iPhone
    "metadata": {
      "chatName": "John Doe",
      "hasMedia": true
    }
  }
}

// Contact card message received (v3.4+ with vCards)
{
  "event": "message.received.contact",
  "timestamp": "2025-01-15T10:39:00.000Z",
  "sessionId": "my-session-001",
  "message": {
    "id": "msg_contact_12345",
    "from": "5511999999999@c.us",
    "fromType": "phone",
    "to": "554791246688@c.us",
    "timestamp": "2025-01-15T10:39:00.000Z",
    "type": "vcard",
    "hasMedia": false,
    "vCards": [                               // v3.4+: Contact cards in vCard format
      "BEGIN:VCARD\nVERSION:3.0\nFN:Jane Smith\nTEL:+5511888888888\nEND:VCARD"
    ],
    "metadata": {
      "chatName": "John Doe",
      "hasMedia": false
    }
  }
}

// Message status update (v2.81+ - automatic tracking)
{
  "event": "message.status_update",
  "timestamp": "2025-11-21T15:45:32.000Z",
  "sessionId": "claudecode",
  "message": {
    "id": "9a0c43b4-9416-4133-ae5e-b768c8ac8839",          // Internal message ID
    "driverMessageId": "3EB0F0E7C8D4F1234567890ABCDEF",    // WhatsApp message ID
    "status": "delivered",                                   // New status: sent → delivered → read
    "updatedAt": "2025-11-21T15:45:32.000Z"                 // When status changed
  },
  "timestamp": "2025-11-21T15:45:32.000Z"
}

// Status lifecycle example (3 separate webhook events for same message):
// 1. status: "sent"      - Message reached WhatsApp servers
// 2. status: "delivered" - Message reached recipient's device
// 3. status: "read"      - Recipient opened message

// Message send failed (v3.7.0+ - explicit failure detection)
{
  "event": "message.failed",
  "timestamp": "2025-12-02T14:30:45.000Z",
  "sessionId": "claudecode",
  "message": {
    "id": "6257fd0f-d62a-4587-86fa-90583aeb95d3",
    "recipient": "41764791479@c.us",
    "error": "Driver returned failed status or empty driverMessageId",
    "failedAt": "2025-12-02T14:30:45.000Z",
    "attempt": 5,
    "maxAttempts": 5
  }
}

// Message sent successfully (v3.8.12+ - includes body, from, type)
// Use case: Lead Automation - detect patterns in self-sent messages
{
  "event": "message.sent",
  "timestamp": 1734523456789,
  "messageId": "9a0c43b4-9416-4133-ae5e-b768c8ac8839",
  "sessionId": "cloudagent",
  "from": "5547991246688",                          // v3.8.12+: Sender phone number
  "to": "5511999999999",
  "type": "text",                                   // v3.8.12+: Message type (text, media, template, etc.)
  "body": "🔔 Nova Oportunidade\nLead: JoÃŖo Silva", // v3.8.12+: Message content
  "hasMedia": false,                                // v3.8.12+: Whether message contains media
  "driverMessageId": "3EB0F0E7C8D4F1234567890ABCDEF",
  "status": "sent"
}

// Media message sent (v3.8.12+)
{
  "event": "message.sent",
  "timestamp": 1734523500000,
  "messageId": "b2c3d4e5-f678-9012-3456-789abcdef012",
  "sessionId": "cloudagent",
  "from": "5547991246688",
  "to": "5511999999999",
  "type": "media",
  "body": "Photo caption here",                    // Caption for media, empty string if none
  "hasMedia": true,
  "driverMessageId": "3EB0F0E7C8D4F9876543210FEDCBA",
  "status": "sent"
}

// Message reaction received (v2.90+)
// Triggered when someone reacts to a message with an emoji
{
  "event": "message.reaction",
  "timestamp": "2025-01-01T10:00:00.000Z",
  "sessionId": "my-session",
  "message": {
    "messageId": "3EB0A0B0C1D2E3F4",                // ID of the message that received the reaction
    "from": "5547991246688@c.us",                   // WhatsApp ID of the person who reacted
    "reaction": "👍",                               // The emoji used (empty string if reaction removed)
    "reactedAt": "2025-01-01T10:00:00.000Z"         // When the reaction was added
  }
}

// Reaction removed (v2.90+)
// When a user removes their reaction, the emoji field is empty
{
  "event": "message.reaction",
  "timestamp": "2025-01-01T10:05:00.000Z",
  "sessionId": "my-session",
  "message": {
    "messageId": "3EB0A0B0C1D2E3F4",
    "from": "5547991246688@c.us",
    "reaction": "",                                 // Empty string = reaction removed
    "reactedAt": "2025-01-01T10:05:00.000Z"
  }
}
✨ v2.90+: Message Reaction Webhook

The message.reaction webhook is triggered when someone reacts to a message with an emoji.

Field Type Description
messageId string ID of the message that received the reaction
from string WhatsApp ID of the person who reacted (@c.us or @lid format)
reaction string The emoji used. Empty string ("") when reaction is removed.
reactedAt string ISO 8601 timestamp when the reaction was added/removed
✨ NEW in v3.8.12: Message.sent Body Fields

v3.8.12+: The message.sent webhook now includes additional fields for richer integrations:

  • from - Sender phone number (E.164 format without +)
  • type - Message type: text, media, template, interactive, list
  • body - Message content (text for text messages, caption for media)
  • hasMedia - Boolean indicating if message contains media

Use Case: Lead Automation

With the new body field, you can now detect patterns in self-sent messages to trigger automation flows:

// Example: Detect "Nova Oportunidade" pattern for Lead Automation
app.post('/webhooks/whatsapp', (req, res) => {
  const { event, body, from, sessionId } = req.body;

  if (event === 'message.sent' && body?.includes('🔔 Nova Oportunidade')) {
    // Trigger Lead Automation workflow
    triggerLeadWorkflow({
      source: 'whatsapp',
      sessionId,
      senderPhone: from,
      messageBody: body
    });
  }

  res.status(200).send('OK');
});
âš ī¸ v3.7.0: Explicit Failure Detection

v3.7.0+: The MessageWorker now explicitly verifies send results before marking messages as sent:

  • Before v3.7.0: If WWebJS sendMessage() failed silently (returned empty driverMessageId), messages were incorrectly marked as "sent"
  • After v3.7.0: Messages are only marked as "sent" if they receive a valid driverMessageId from WhatsApp

Retry Behavior: Failed messages are retried up to 5 times with exponential backoff. After max retries, they go to the Dead Letter Queue (DLQ) and a message.failed webhook is dispatched.

Common Failure Causes:

  • Invalid recipient number (doesn't exist on WhatsApp)
  • Number format mismatch (@c.us vs @lid)
  • Session disconnected during send
  • WhatsApp rate limiting

Webhook Security (HMAC)

Webhooks include an X-Signature header with HMAC-SHA256 signature (base64-encoded) for verification.

Signature Header: X-Signature: <base64-encoded-hmac>

HMAC Secret: Your tenant's API key (or webhook secret if configured)

// Node.js example - Verify webhook signature
import crypto from 'crypto';

const verifyWebhookSignature = (payloadString, receivedSignature, secret) => {
  // Generate expected signature
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payloadString)
    .digest('base64');  // v2.8+ uses base64 encoding

  // Compare signatures (timing-safe)
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(receivedSignature)
  );
};

// Express.js webhook endpoint
app.post('/webhooks/whatsapp', express.json(), (req, res) => {
  const receivedSignature = req.headers['x-signature'];
  const payloadString = JSON.stringify(req.body);

  // Verify signature using your API key or webhook secret
  if (!verifyWebhookSignature(payloadString, receivedSignature, process.env.API_KEY)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Signature valid - process webhook
  const event = req.body;
  console.log('Verified webhook:', event.event, 'from:', event.from);

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

// Fastify example
fastify.post('/webhooks/whatsapp', async (request, reply) => {
  const receivedSignature = request.headers['x-signature'];
  const payloadString = JSON.stringify(request.body);

  if (!verifyWebhookSignature(payloadString, receivedSignature, process.env.API_KEY)) {
    return reply.status(401).send({ error: 'Invalid signature' });
  }

  const event = request.body;
  console.log('Webhook received:', event.event, event.from, '→', event.to);

  return { status: 'ok' };
});

WhatsApp Identifier Formats - v3.0.3+ 🆕

The from field in webhooks uses WhatsApp JID (Jabber ID) format. Understanding these formats is crucial for proper message handling.

âš ī¸ Important: @lid Format

The @lid format (Linked Identity) is NOT a phone number. Use metadata.chatName to identify the contact. This format is common in Switzerland, some European countries, and WhatsApp Business accounts.

Identifier Types

Format fromType Example Description Contains Phone?
@c.us "phone" 5547999999999@c.us Traditional individual chat ✅ Yes - can be normalized to E.164
@lid "lid" 77442588909785@lid Linked Identity (internal ID) ❌ No - use metadata.chatName
@g.us "group" 120363220421466993@g.us Group chat ❌ No - this is a group ID

Processing by fromType (v3.0.3+)

// TypeScript example - Handle different identifier types
interface WebhookPayload {
  event: string;
  sessionId: string;
  message: {
    from: string;
    fromType: 'phone' | 'lid' | 'group' | 'unknown';  // v3.0.3+
    metadata?: {
      chatName?: string;
      contactName?: string;
    };
  };
}

function extractContactInfo(webhook: WebhookPayload) {
  const { from, fromType, metadata } = webhook.message;

  switch (fromType) {
    case 'phone':
      // Traditional format - normalize to E.164
      const phone = from.replace('@c.us', '');
      return {
        type: 'phone',
        identifier: '+' + phone,
        displayName: metadata?.contactName || metadata?.chatName || phone
      };

    case 'lid':
      // Linked Identity - use chatName for identification
      return {
        type: 'lid',
        identifier: from,  // Keep as-is (internal ID)
        displayName: metadata?.chatName || 'Unknown Contact'
      };

    case 'group':
      // Group message - extract author for actual sender
      return {
        type: 'group',
        groupId: from,
        displayName: metadata?.chatName || 'Unknown Group'
      };

    default:
      return {
        type: 'unknown',
        identifier: from,
        displayName: metadata?.chatName || from
      };
  }
}

Phone Number Normalization (for @c.us only)

// Only normalize @c.us format to E.164
function normalizePhoneNumber(jid: string): string | null {
  if (!jid.endsWith('@c.us')) {
    return null;  // Cannot normalize @lid or @g.us
  }
  const phone = jid.replace('@c.us', '');
  return '+' + phone;
}

// Examples:
normalizePhoneNumber('5547999999999@c.us')   // → '+5547999999999'
normalizePhoneNumber('77442588909785@lid')   // → null (not a phone!)
normalizePhoneNumber('120363...@g.us')       // → null (group ID)

Sending Messages to @lid Contacts - v3.0.4+ 🆕

Starting from v3.0.4, you can send messages using the complete JID format in the to field. This is essential for replying to contacts who appear with @lid format.

💡 Key Insight

When you receive a message from 77442588909785@lid, you cannot simply use the numeric portion as a phone number. You must send the reply using the complete JID including the @lid suffix.

Accepted Formats for to Field

Format Example Use Case
E.164 (phone) 5547999999999 Traditional contacts - converted to @c.us internally
JID @c.us 5547999999999@c.us Explicit contact JID (used as-is)
JID @lid 77442588909785@lid Linked Identity contacts âš ī¸ (used as-is)
JID @g.us 120363220421466993@g.us Send to groups (used as-is)

Example: Replying to an @lid Contact

// Step 1: You receive a webhook with @lid format
{
  "event": "message.received.text",
  "sessionId": "sensyxboutique",
  "message": {
    "from": "77442588909785@lid",      // ← Note the @lid format
    "fromType": "lid",
    "body": "OlÃĄ!",
    "metadata": {
      "chatName": "Giovanni Moser"     // ← Use this to identify the contact
    }
  }
}

// Step 2: Reply using the COMPLETE JID
POST /api/v1/messages/text
{
  "sessionId": "sensyxboutique",
  "to": "77442588909785@lid",          // ← Use the complete JID from webhook
  "text": "OlÃĄ! Bem-vindo à Sensyx Boutique!"
}

âš ī¸ Common Mistake

Wrong: "to": "77442588909785" → Message queued but NOT delivered (wrong format)

Correct: "to": "77442588909785@lid" → Message delivered successfully ✅

@lid Limitations

  • No phone number: Cannot be converted to E.164 format
  • Identification: Use metadata.chatName to identify the contact
  • Storage: Store the complete @lid JID in your database for future communication
  • Regional: Common in Switzerland, some European countries, and WhatsApp Business accounts

Best Practice: Store the JID

// When receiving a message, store the complete JID
async function handleIncomingMessage(webhook: WebhookPayload) {
  const { from, fromType, metadata } = webhook.message;

  // Store the complete JID for future replies
  await db.contacts.upsert({
    jid: from,                           // "77442588909785@lid" or "5547999999999@c.us"
    type: fromType,                      // "lid" or "phone"
    displayName: metadata?.chatName,     // "Giovanni Moser"
    sessionId: webhook.sessionId
  });
}

// When sending a reply, use the stored JID
async function sendReply(contactId: string, text: string) {
  const contact = await db.contacts.findById(contactId);

  await api.post('/api/v1/messages/text', {
    sessionId: contact.sessionId,
    to: contact.jid,                     // Use stored JID directly
    text: text
  });
}

Webhook Authentication (Bearer Token) - v2.86+

Configure Bearer token authentication for secure webhook delivery to protected endpoints (Supabase Edge Functions, AWS Lambda, Azure Functions, etc.).

🔒 Security Requirements

  • HTTPS Required: Webhook URL must use HTTPS when auth token is configured
  • Token Length: Minimum 20 characters, maximum 500 characters
  • Storage: Tokens are stored encrypted in database
  • Logging: Only first 8 characters logged (security)

When to Use Bearer Token Authentication

Bearer tokens are required when your webhook endpoint needs authentication:

  • Supabase Edge Functions: Requires anon or service_role key
  • AWS Lambda + API Gateway: JWT authorizer tokens
  • Azure Functions: Function keys (x-functions-key or Authorization)
  • Custom APIs: Any endpoint requiring Authorization: Bearer <token>

Configuration via Admin Dashboard (v2.88: Token Generation UI)

Generate and configure secure webhook tokens directly in the Admin Dashboard:

✨ NEW in v2.88: Automatic Token Generation

The Admin Dashboard now includes a "Gerar Token Seguro" button that automatically generates cryptographically secure tokens (40+ characters) with proper format: wh_sessao_20251123_{random}

Step-by-Step: Generate Token
  1. Navigate to Sessions tab in Admin Dashboard
  2. Click Create Session or edit existing session
  3. Fill in Session ID (required for token generation)
  4. Fill in Webhook URL (must be HTTPS)
  5. Click "🔑 Gerar Token Seguro" button - Token will be auto-generated and populated
  6. (Optional) Click 📋 Copy button to copy token to clipboard
  7. (Optional) Click đŸ‘ī¸ Mostrar to toggle token visibility
  8. Save session
Token Format (v2.88)

Auto-generated tokens follow this secure format:

wh_{sessionId}_{YYYYMMDD}_{20-random-chars}

Example:
wh_sensyx_20251123_a7f3k9j2m1n4p6q8r0s2t4u6v8w0x2y4z6
   └─â”Ŧ──┘ └───â”Ŧ───┘ └─────────────â”Ŧ────────────────┘
     │        │                    └─ 20 crypto-secure random chars
     │        └─ Creation date (YYYYMMDD)
     └─ Session identifier (prefix: wh_)
Manual Token Entry (Alternative)

You can also manually enter tokens (e.g., Supabase anon key, custom JWT):

  1. Paste token into Webhook Auth Token field
  2. Ensure token is minimum 20 characters
  3. Save session

Configuration via API

// Create session with webhook authentication
const response = await fetch('https://apiwts.top/admin/api/sessions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Cookie': 'session=...' // Admin authentication
  },
  body: JSON.stringify({
    tenantId: 'your-tenant-id',
    sessionId: 'my-session',
    webhookUrl: 'https://your-edge-function.supabase.co/webhook',
    webhookAuthToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' // Your Bearer token
  })
});

// Update existing session
const updateResponse = await fetch('https://apiwts.top/admin/api/sessions/my-session', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Cookie': 'session=...'
  },
  body: JSON.stringify({
    webhookUrl: 'https://new-endpoint.com/webhook',
    webhookAuthToken: 'your-new-token' // Leave empty to keep existing
  })
});

How apiwts.top Sends Authenticated Webhooks

When webhookAuthToken is configured, apiwts.top includes the Authorization header:

POST /webhook HTTP/1.1
Host: your-edge-function.supabase.co
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-Webhook-Event: message.received
X-Message-ID: msg_123456789
X-Signature: abc123... (HMAC signature, if secret configured)
User-Agent: WhatsApp-Multi-Driver-API/1.0

{
  "event": "message.received",
  "sessionId": "my-session",
  "from": "5551234567890",
  "body": "Hello World",
  "timestamp": 1730000000000
}

Example: Supabase Edge Function

// Supabase Edge Function (TypeScript)
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'

serve(async (req) => {
  // 1. Verify Authorization header
  const authHeader = req.headers.get('Authorization');
  const expectedToken = 'Bearer ' + Deno.env.get('SUPABASE_ANON_KEY');

  if (authHeader !== expectedToken) {
    return new Response(JSON.stringify({ error: 'Unauthorized' }), {
      status: 401,
      headers: { 'Content-Type': 'application/json' }
    });
  }

  // 2. Process webhook
  const webhook = await req.json();
  console.log('Webhook received:', webhook.event, webhook.from);

  // Your business logic here...

  return new Response(JSON.stringify({ status: 'ok' }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' }
  });
});

Example: Express.js with Bearer Token

// Express.js endpoint (Node.js)
app.post('/webhook', express.json(), (req, res) => {
  // 1. Verify Bearer token
  const authHeader = req.headers.authorization;
  const expectedToken = 'Bearer ' + process.env.WEBHOOK_TOKEN;

  if (authHeader !== expectedToken) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  // 2. Optional: Verify HMAC signature (if configured)
  const signature = req.headers['x-signature'];
  if (signature) {
    const payloadString = JSON.stringify(req.body);
    if (!verifyWebhookSignature(payloadString, signature, process.env.WEBHOOK_SECRET)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
  }

  // 3. Process webhook
  const event = req.body;
  console.log('Authenticated webhook:', event.event, event.from);

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

Example: AWS Lambda + API Gateway

// Lambda function (Node.js)
export const handler = async (event) => {
  // API Gateway JWT Authorizer already validated token
  // event.requestContext.authorizer.claims contains JWT claims

  const webhook = JSON.parse(event.body);
  console.log('Webhook received:', webhook.event, webhook.from);

  // Your business logic here...

  return {
    statusCode: 200,
    body: JSON.stringify({ status: 'ok' })
  };
};

Error Handling & Retry Logic

apiwts.top implements intelligent retry logic for webhook deliveries:

  • 401 Unauthorized: Invalid/expired token - NO RETRY (fix token configuration)
  • 403 Forbidden: Valid token but insufficient permissions - NO RETRY (check token permissions)
  • 408 Request Timeout: Retries up to 3 times (1s, 5s, 30s intervals)
  • 429 Too Many Requests: Retries up to 3 times
  • 5xx Server Errors: Retries up to 3 times (temporary failures)
  • Other 4xx Errors: NO RETRY (client errors are permanent)

Troubleshooting

âš ī¸ Common Issues

  • HTTP 401 "Webhook URL must use HTTPS when auth token is configured"
    Solution: Change webhook URL from http:// to https://
  • Webhooks not arriving at Supabase Edge Function
    Solution: Configure webhookAuthToken with your Supabase anon key
  • Webhook delivers but returns 401 Unauthorized
    Solution: Verify token is correct and matches your endpoint's expected token
  • Token rejected: "must be at least 20 characters"
    Solution: Use a secure token with minimum 20 characters (recommended: 32+ chars)

❌ Identifier Format Issues (v3.0.3+)

  • "Invalid phone format" error when processing webhooks
    Cause: Code is trying to normalize @lid format as a phone number.
    Solution: Check fromType field before normalizing. Only normalize when fromType === "phone".
  • Messages from some contacts not appearing in your system
    Cause: Validation filters are rejecting @lid format as invalid.
    Solution: Accept all three formats (@c.us, @lid, @g.us). Use fromType to differentiate.
  • Contact name shows as "Unknown" for @lid messages
    Cause: Code is parsing the from field expecting a phone number.
    Solution: For @lid format, use metadata.chatName or metadata.contactName for the contact's display name.
  • Swiss/European contacts always show @lid instead of phone number
    Cause: This is expected behavior. Some WhatsApp accounts (especially Business accounts and certain regions) use Linked Identity.
    Solution: Store both the from (identifier) and metadata.chatName (display name) in your database.

Security Best Practices

  • Use HTTPS: Always use https:// webhook URLs when auth tokens are configured
  • Rotate Tokens: Regularly rotate Bearer tokens (recommended: every 90 days)
  • Minimum Length: Use tokens with at least 32 characters for security
  • Environment Variables: Store tokens in environment variables, never hardcode
  • Dual Authentication: Use both Bearer token + HMAC signature for maximum security
  • Monitor Failures: Check webhook delivery logs for authentication failures

Webhook Headers

apiwts.top sends these headers with every webhook delivery:

  • Content-Type: application/json
  • X-Webhook-Event: message.received - Event type
  • X-Message-ID: msg_987654321 - Message identifier
  • X-Signature: <base64-hmac> - HMAC signature for verification
  • User-Agent: WhatsApp-Multi-Driver-API/1.0

GET /api/v1/webhooks/config

Retrieve current webhook configuration for your tenant.

// Request
const response = await fetch('https://apiwts.top/api/v1/webhooks/config', {
  method: 'GET',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

const config = await response.json();
console.log(config);

// Response (200 OK)
{
  "id": "webhook_config_abc123",
  "tenantId": "tenant_xyz789",
  "url": "https://your-app.com/webhooks/whatsapp",
  "events": [
    "message.received.text",
    "message.received.media",
    "message.sent"
  ],
  "retryCount": 3,
  "timeoutMs": 10000,
  "active": true,
  "createdAt": "2025-10-01T10:00:00.000Z",
  "updatedAt": "2025-10-01T10:00:00.000Z"
}

// Response (404 Not Found) - No webhook configured
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "No webhook configuration found for this tenant"
}

DELETE /api/v1/webhooks/config

Remove webhook configuration (soft delete - sets active = false).

// Request
const response = await fetch('https://apiwts.top/api/v1/webhooks/config', {
  method: 'DELETE',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Response (200 OK)
{
  "message": "Webhook configuration deleted successfully"
}

// Response (404 Not Found)
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "No webhook configuration found for this tenant"
}

GET /api/v1/webhooks/events

List all available webhook event types that can be subscribed to.

✨ Updated in v2.90 - 22 webhook events now available

// Request
const response = await fetch('https://apiwts.top/api/v1/webhooks/events', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200 (22 events total)
{
  "events": [
    // Message Events (11 types)
    { "type": "message.received.text", "description": "Text message received from contact", "category": "messages" },
    { "type": "message.received.media", "description": "Media message (image/video/audio/document) received", "category": "messages" },
    { "type": "message.received.location", "description": "Location message received", "category": "messages" },
    { "type": "message.received.contact", "description": "Contact card/vCard received", "category": "messages" },
    { "type": "message.sent", "description": "Message successfully sent via WhatsApp", "category": "messages" },
    { "type": "message.delivered", "description": "Message delivered to recipient", "category": "messages" },
    { "type": "message.read", "description": "Message read by recipient (blue ticks)", "category": "messages" },
    { "type": "message.failed", "description": "Message failed to send", "category": "messages" },
    { "type": "message.edited", "description": "Message was edited (v2.90+)", "category": "messages" },
    { "type": "message.reaction", "description": "Message received a reaction (v2.90+)", "category": "messages" },
    { "type": "message.revoked", "description": "Message was revoked/deleted (v2.90+)", "category": "messages" },

    // Session Events (7 types)
    { "type": "session.ready", "description": "WhatsApp session is ready to send/receive messages", "category": "session" },
    { "type": "session.disconnected", "description": "WhatsApp session disconnected", "category": "session" },
    { "type": "session.qr_updated", "description": "QR code updated (scan required)", "category": "session" },
    { "type": "session.auth_failure", "description": "Authentication failed", "category": "session" },
    { "type": "session.zombie_detected", "description": "Zombie session detected - auto-recovery initiated (v3.8.24+)", "category": "session" },
    { "type": "session.recovered", "description": "Zombie session recovered successfully (v3.8.24+)", "category": "session" },
    { "type": "session.recovery_failed", "description": "Zombie session recovery failed (v3.8.24+)", "category": "session" },

    // Group Events (3 types)
    { "type": "group.participant_added", "description": "Participant added to group", "category": "groups" },
    { "type": "group.participant_removed", "description": "Participant removed from group", "category": "groups" },
    { "type": "group.info_updated", "description": "Group info was updated", "category": "groups" },

    // Contact Events (1 type)
    { "type": "contact.status_updated", "description": "Contact status text was updated", "category": "contacts" },

    // Call Events (2 types)
    { "type": "call.received", "description": "Incoming call received", "category": "calls" },
    { "type": "call.ended", "description": "Call ended", "category": "calls" },

    // Poll Events (1 type)
    { "type": "poll.vote_update", "description": "Poll vote was updated (v2.90+)", "category": "polls" }
  ]
}

POST /api/v1/webhooks/test

Send a test webhook event to verify your endpoint configuration.

// Request
const response = await fetch('https://apiwts.top/api/v1/webhooks/test', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://your-app.com/webhooks/whatsapp',
    secret: 'your-webhook-secret'  // Optional
  })
});

// Response 200
{
  "success": true,
  "message": "Test webhook delivered successfully",
  "testEventId": "test_1642598523000_abc123",
  "deliveryStatus": "delivered"
}

GET /api/v1/webhooks/analytics/trends

✨ NEW in v2.12

Get temporal trends (daily success/failure counts) and latency percentiles for webhook deliveries.

// Request with custom period
const response = await fetch('https://apiwts.top/api/v1/webhooks/analytics/trends?days=14', {
  headers: { 'X-API-Key': 'your-api-key' }
});

// Response 200
{
  "period": {
    "start": "2025-01-01",
    "end": "2025-01-15",
    "days": 14
  },
  "trends": [
    {
      "date": "2025-01-15",
      "success": 145,
      "failed": 5,
      "avgLatencyMs": 250.5
    },
    {
      "date": "2025-01-14",
      "success": 132,
      "failed": 2,
      "avgLatencyMs": 180.3
    }
  ],
  "latency": {
    "p50": 180.5,
    "p95": 450.2,
    "p99": 890.7,
    "samples": 2500
  }
}
Query Parameter Type Default Description
days number 7 Number of days to analyze (min: 1, max: 30)

📊 Webhook Analytics Dashboard

✨ NEW in v2.10

Get comprehensive analytics and metrics about your webhook delivery performance, including success rates, latency, top failing events, and circuit breaker status.

GET /api/v1/webhooks/stats

Retrieve complete webhook delivery statistics and analytics.

🔑 Requires API Key Authentication

Request Example

// JavaScript/Node.js
const response = await fetch('https://apiwts.top/api/v1/webhooks/stats', {
  method: 'GET',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

const stats = await response.json();
console.log('Webhook Analytics:', stats);

// cURL
curl -X GET https://apiwts.top/api/v1/webhooks/stats \
  -H "X-API-Key: your-api-key"

Response Schema

{
  "totalDeliveries": number,        // Total webhook delivery attempts
  "successRate": number,            // Success percentage (0-100, 2 decimals)
  "avgLatencyMs": number | null,    // Average delivery latency in milliseconds
  "failed": {
    "total": number,                // Total failed deliveries
    "last24h": number               // Failed deliveries in last 24 hours
  },
  "circuitBreakers": number,        // Number of circuit breaker activations
  "topFailingEvents": [             // Top 5 events with most failures
    {
      "event": string,              // Event type
      "failures": number,           // Number of failures
      "rate": number                // Percentage of total failures (2 decimals)
    }
  ]
}

Success Response (200 OK) - With Deliveries

{
  "totalDeliveries": 15234,
  "successRate": 97.52,
  "avgLatencyMs": 245,
  "failed": {
    "total": 381,
    "last24h": 12
  },
  "circuitBreakers": 3,
  "topFailingEvents": [
    {
      "event": "message.received.media",
      "failures": 145,
      "rate": 38.06
    },
    {
      "event": "session.qr_updated",
      "failures": 89,
      "rate": 23.36
    },
    {
      "event": "message.sent",
      "failures": 67,
      "rate": 17.59
    },
    {
      "event": "session.ready",
      "failures": 45,
      "rate": 11.81
    },
    {
      "event": "message.delivered",
      "failures": 35,
      "rate": 9.19
    }
  ]
}

Success Response (200 OK) - No Deliveries Yet

{
  "totalDeliveries": 0,
  "successRate": 0,
  "avgLatencyMs": null,
  "failed": {
    "total": 0,
    "last24h": 0
  },
  "circuitBreakers": 0,
  "topFailingEvents": []
}

Error Response (404 Not Found)

{
  "statusCode": 404,
  "error": "Not Found",
  "message": "No webhook configuration found for this tenant"
}

Metrics Explained

  • totalDeliveries: COUNT(*) from webhook_deliveries table
  • successRate: Calculated as (successCount / totalDeliveries) * 100, rounded to 2 decimals
  • avgLatencyMs: Average time between created_at and delivered_at in milliseconds (only for completed deliveries)
  • failed.total: Total count of deliveries with status = 'failed'
  • failed.last24h: Failed deliveries created in the last 24 hours
  • circuitBreakers: Number of webhooks disabled due to exceeding 10 consecutive failures
  • topFailingEvents: Top 5 event types ordered by failure count, with percentage of total failures

Use Cases

  • 📈 Monitor webhook health: Track success rate and latency trends
  • 🔍 Identify problematic events: Find which events are failing most frequently
  • âš ī¸ Circuit breaker alerts: Get notified when webhooks are auto-disabled
  • 📊 Performance optimization: Use latency metrics to optimize webhook endpoints
  • 🚨 Incident response: Check failed.last24h to detect recent delivery issues

Circuit Breaker Feature

The system automatically disables webhooks (active = false) when:

  • 🚨 Threshold: 10 consecutive failures detected
  • âąī¸ Check Frequency: Checked automatically every minute
  • 🔄 Manual Reset: Re-configure webhook to re-enable (contact administrator if needed)
  • 📊 Tracking: circuitBreakers metric shows total disabled webhooks

Example: Monitoring Dashboard

// Fetch webhook stats every 30 seconds for dashboard
setInterval(async () => {
  const response = await fetch('https://apiwts.top/api/v1/webhooks/stats', {
    headers: { 'X-API-Key': process.env.API_KEY }
  });

  const stats = await response.json();

  // Alert if success rate drops below 95%
  if (stats.successRate < 95) {
    console.error('âš ī¸ Webhook success rate LOW:', stats.successRate + '%');
    console.error('Top failing events:', stats.topFailingEvents);
  }

  // Alert if circuit breaker activated
  if (stats.circuitBreakers > 0) {
    console.error('🚨 Circuit breaker activated! Webhooks disabled:', stats.circuitBreakers);
  }

  // Monitor latency
  if (stats.avgLatencyMs > 1000) {
    console.warn('âąī¸ High webhook latency:', stats.avgLatencyMs + 'ms');
  }
}, 30000);

Future Enhancements (Roadmap)

  • 📈 Temporal Graphs: 7-day success/failure trends with DATE_TRUNC
  • 📊 Latency Percentiles: p50, p95, p99 using PERCENTILE_CONT
  • 📝 Audit Log Integration: Full circuit breaker activation history
  • 🔔 Real-time Alerts: WebSocket notifications for critical events

POST /api/v1/webhooks/reset-circuit-breaker

✨ NEW in v2.11

Manually reactivate a webhook that was disabled by circuit breaker after 10 consecutive failures. Resets the failed_deliveries counter and sets active = true without changing webhook configuration.

Request Example

// JavaScript/Node.js
const response = await fetch('https://apiwts.top/api/v1/webhooks/reset-circuit-breaker', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

const result = await response.json();
console.log(result);

// cURL
curl -X POST https://apiwts.top/api/v1/webhooks/reset-circuit-breaker \
  -H "X-API-Key: your-api-key"

Success Response (200 OK)

{
  "success": true,
  "message": "Circuit breaker reset successfully. Webhook is now active.",
  "webhookConfigId": "webhook_config_abc123"
}

Error Response (404 Not Found)

{
  "statusCode": 404,
  "error": "Not Found",
  "message": "No webhook configuration found for this tenant"
}

Error Response (409 Conflict)

{
  "statusCode": 409,
  "error": "Conflict",
  "message": "Webhook is already active. Circuit breaker reset is only available for disabled webhooks."
}

When to Use

  • 🔧 After fixing webhook endpoint: Your server was down and circuit breaker triggered
  • 🐛 Debugging webhook issues: Iteratively test fixes without full reconfiguration
  • ⚡ Quick recovery: Preserves URL, events, secret - only resets counter
  • 🔄 Alternative to reconfigure: No need to provide all settings again

Comparison: Reset vs Reconfigure

Feature Reset Circuit Breaker (v2.11) Reconfigure Webhook (v2.7)
Preserves URL ✅ Yes ❌ Must provide again
Preserves Events ✅ Yes ❌ Must provide again
Preserves Secret ✅ Yes ❌ Auto-regenerated
Resets Counter ✅ Yes (failed_deliveries = 0) ✅ Yes (new config)
Use Case Quick recovery after fix Change webhook settings

đŸ‘Ĩ Contact Management WWebJS Only

Manage WhatsApp contacts: list, retrieve, block, unblock, and get profile pictures.

Driver Limitation: Contact management is only available with the WWebJS driver. Cloud API does not support these operations.

GET /api/v1/contacts v3.8.26+

List all contacts from your WhatsApp session with optional pagination and filters.

Query Parameters

Parameter Type Required Description
sessionId string No Session ID (defaults to tenant's first session)
limit number No Maximum contacts to return (default: 100, max: 500) (v3.8.26+)
isBlocked boolean No Filter by blocked status: true = only blocked, false = only unblocked (v3.8.26+)
isMyContact boolean No Filter by saved contacts: true = only saved in phone, false = not saved (v3.8.26+)

Example Request (with filters)

// Get first 50 saved contacts that are not blocked
const response = await fetch('https://apiwts.top/api/v1/contacts?limit=50&isMyContact=true&isBlocked=false', {
  method: 'GET',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

Example Response (200 OK)

{
  "success": true,
  "contacts": [
    {
      "id": "5511999999999@c.us",
      "name": "JoÃŖo Silva",
      "number": "5511999999999",
      "pushname": "JoÃŖo",
      "isBlocked": false,
      "isMyContact": true,
      "profilePicUrl": "https://pps.whatsapp.net/v/..."
    },
    {
      "id": "5511888888888@c.us",
      "name": "Maria Santos",
      "number": "5511888888888",
      "pushname": "Maria",
      "isBlocked": false,
      "isMyContact": true,
      "profilePicUrl": null
    }
  ],
  "total": 150,
  "returned": 2,
  "filters": {
    "limit": 50,
    "isBlocked": false,
    "isMyContact": true
  }
}

Response Fields (v3.8.26+)

Field Type Description
contacts array List of contacts matching filters
contacts[].isMyContact boolean Whether contact is saved in phone's address book (NEW v3.8.26)
total number Total contacts before filtering
returned number Number of contacts returned after filters/limit (NEW v3.8.26)
filters object Applied filters summary (NEW v3.8.26)

Errors

Status Error Description
404 Not Found No sessions found for this tenant
400 Bad Request Session not READY or driver not WWebJS

GET /api/v1/contacts/:phoneNumber

Get details of a specific contact by phone number.

Parameters

Parameter Type Required Description
phoneNumber string Yes Phone number with country code (e.g., 5511999999999, 41764791479, 14155552671)

Example Request

const response = await fetch('https://apiwts.top/api/v1/contacts/5511999999999', {
  method: 'GET',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

Example Response (200 OK)

{
  "success": true,
  "contact": {
    "id": "5511999999999@c.us",
    "name": "JoÃŖo Silva",
    "number": "5511999999999",
    "pushname": "JoÃŖo",
    "isBlocked": false,
    "profilePicUrl": "https://pps.whatsapp.net/v/..."
  }
}

Errors

Status Error Description
404 Not Found Contact not found in session
400 Bad Request Invalid session state or driver

POST /api/v1/contacts/:phoneNumber/block

Block a contact on WhatsApp. Useful for spam prevention.

Example Request

const response = await fetch('https://apiwts.top/api/v1/contacts/5511999999999/block', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

Example Response (200 OK)

{
  "success": true,
  "message": "Contact blocked successfully",
  "phoneNumber": "5511999999999"
}

POST /api/v1/contacts/:phoneNumber/unblock

Unblock a previously blocked contact.

Example Request

const response = await fetch('https://apiwts.top/api/v1/contacts/5511999999999/unblock', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

Example Response (200 OK)

{
  "success": true,
  "message": "Contact unblocked successfully",
  "phoneNumber": "5511999999999"
}

GET /api/v1/contacts/:phoneNumber/profile-pic

Get the profile picture URL of a contact. Returns null if contact has no profile picture or privacy settings prevent access.

Example Request

const response = await fetch('https://apiwts.top/api/v1/contacts/5511999999999/profile-pic', {
  method: 'GET',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

Example Response (200 OK)

{
  "success": true,
  "profilePicUrl": "https://pps.whatsapp.net/v/t61.24694-24/...",
  "phoneNumber": "5511999999999",
  "reason": null
}

Example Response (No Profile Picture)

{
  "success": true,
  "profilePicUrl": null,
  "phoneNumber": "5511999999999",
  "reason": null
}

Example Response (@lid Identifier - v3.8.31+)

{
  "success": true,
  "profilePicUrl": null,
  "phoneNumber": "170321256693940@lid",
  "reason": "LID_NOT_SUPPORTED"
}
Profile Picture Limitations (v3.8.28+):
  • Privacy Settings: Contacts with private profile settings will return profilePicUrl: null
  • Contact Sync: The contact must have recent interaction with the session for reliable retrieval
  • Rate Limiting: WhatsApp may rate-limit excessive profile picture requests
  • Cache Delay: Newly added contacts may take a few seconds to sync before their profile picture is available
âš ī¸ @lid Identifier Limitation (v3.8.31+):
  • What is @lid? Linked Identity - WhatsApp's privacy feature to hide phone numbers in groups
  • Profile Pictures: @lid contacts always return profilePicUrl: null - this is by WhatsApp design, not a bug
  • Why? WhatsApp intentionally hides profile data for @lid identifiers to protect user privacy
  • Workaround? None available - there is no method to convert @lid to phone number (by design)
  • API Response: When @lid is detected, response includes "reason": "LID_NOT_SUPPORTED" to identify this case
  • References: GitHub Issue #3519, Issue #3834

Use Cases

Use Case Endpoint Description
CRM Integration GET /contacts Sync WhatsApp contacts with external CRM system
Spam Prevention POST /contacts/:phone/block Automatically block known spam numbers
Profile Backup GET /contacts/:phone/profile-pic Download and store contact profile pictures
Contact Validation GET /contacts/:phone Check if number exists in contacts before messaging
âš ī¸ Important Notes:
  • WWebJS Only: Contact Management is not available with Cloud API driver (Meta API limitation)
  • Session State: All endpoints require session in READY state
  • Rate Limiting: WhatsApp may rate-limit excessive profile picture requests
  • Privacy: Some contacts may have privacy settings preventing profile picture access

đŸ’Ŧ Chat Management WWebJS Only

Manage WhatsApp chats with archive, mute, pin, and retrieval operations.

Driver Limitation: Chat management is only available with the WWebJS driver. Cloud API does not support these operations.
Available Features:
  • List all chats with filtering (archived, unread, groups)
  • Get individual chat details by ID
  • Archive/unarchive chats for organization
  • Mute/unmute chats with optional duration
  • Pin/unpin important conversations

GET /api/v1/chats

Retrieve all chats from the WhatsApp session with optional filtering.

Query Parameters

Parameter Type Required Description
sessionId string No Session ID (defaults to tenant's first session)
archived boolean No Filter by archived status
unreadOnly boolean No Show only chats with unread messages
groups boolean No Filter by group chats (true) or individual (false)
limit number No Maximum chats to return (1-500, default 100)

Example Request

const response = await fetch('https://apiwts.top/api/v1/chats?unreadOnly=true&limit=50', {
  method: 'GET',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

Example Response

{
  "success": true,
  "chats": [
    {
      "id": "5511999999999@c.us",
      "name": "John Doe",
      "isGroup": false,
      "unreadCount": 3,
      "lastMessage": {
        "body": "Hey, are you there?",
        "timestamp": "2025-10-03T14:30:00.000Z",
        "fromMe": false
      },
      "timestamp": 1727967000000,
      "archived": false,
      "muted": false,
      "muteExpiration": null,
      "pinned": true
    }
  ],
  "total": 1
}

GET /api/v1/chats/:chatId

Get detailed information about a specific chat.

Example Request

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us', {
  method: 'GET',
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

Example Response

{
  "success": true,
  "chat": {
    "id": "5511999999999@c.us",
    "name": "John Doe",
    "isGroup": false,
    "unreadCount": 3,
    "lastMessage": {
      "body": "Hey, are you there?",
      "timestamp": "2025-10-03T14:30:00.000Z",
      "fromMe": false
    },
    "timestamp": 1727967000000,
    "archived": false,
    "muted": false,
    "muteExpiration": null,
    "pinned": true
  }
}

POST /api/v1/chats/:chatId/archive

Archive a chat to hide it from the main chat list.

Example Request

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/archive', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001'
  })
});

Example Response

{
  "success": true,
  "message": "Chat archived successfully",
  "chatId": "5511999999999@c.us"
}

POST /api/v1/chats/:chatId/unarchive

Unarchive a chat to restore it to the main chat list.

Example Request

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/unarchive', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001'
  })
});

Example Response

{
  "success": true,
  "message": "Chat unarchived successfully",
  "chatId": "5511999999999@c.us"
}

POST /api/v1/chats/:chatId/mute

Mute chat notifications. Duration is optional (indefinite if not provided).

Request Body

Field Type Required Description
sessionId string No Session ID
duration number No Mute duration in milliseconds

Example Request (Mute for 24 hours)

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/mute', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    duration: 86400000  // 24 hours in milliseconds
  })
});

Example Response (Temporary Mute)

{
  "success": true,
  "message": "Chat muted until 2025-10-04T14:30:00.000Z",
  "chatId": "5511999999999@c.us"
}

Example Request (Indefinite Mute)

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/mute', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001'
  })
});

Example Response (Indefinite Mute)

{
  "success": true,
  "message": "Chat muted indefinitely",
  "chatId": "5511999999999@c.us"
}

POST /api/v1/chats/:chatId/unmute

Unmute a chat to restore notifications.

Example Request

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/unmute', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001'
  })
});

Example Response

{
  "success": true,
  "message": "Chat unmuted successfully",
  "chatId": "5511999999999@c.us"
}

POST /api/v1/chats/:chatId/pin

Pin a chat to keep it at the top of the chat list.

Example Request

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/pin', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001'
  })
});

Example Response

{
  "success": true,
  "message": "Chat pinned successfully",
  "chatId": "5511999999999@c.us"
}

POST /api/v1/chats/:chatId/unpin

Unpin a chat to remove it from the pinned section.

Example Request

const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/unpin', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001'
  })
});

Example Response

{
  "success": true,
  "message": "Chat unpinned successfully",
  "chatId": "5511999999999@c.us"
}

POST /api/v1/chats/:chatId/state

v2.85+ Set chat presence state - typing/recording indicators (WWebJS only).

Important: Only supported on wwebjs driver. Cloud API driver will return 422 error.

Request Body

Parameter Type Required Description
sessionId string Yes Session identifier
state string Yes State to set: typing, recording, or stop

Example Request

// Simulate typing indicator
const response = await fetch('https://apiwts.top/api/v1/chats/5511999999999@c.us/state', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'my-session-001',
    state: 'typing'
  })
});

// Response (200 OK - Success)
{
  "success": true,
  "message": "Chat state \"typing\" set successfully",
  "data": {
    "chatId": "5511999999999@c.us",
    "state": "typing",
    "appliedAt": "2025-11-23T08:00:00.000Z"
  }
}

States

  • typing: Shows "typing..." indicator to the recipient
  • recording: Shows "recording..." indicator (for voice messages)
  • stop: Clears any active presence state

Use Cases

  • Human-like Bot Behavior: Simulate natural conversation by showing typing indicator before sending response
  • Voice Message Indication: Show recording state before sending voice message
  • Engagement: Increase user engagement by making automated responses feel more natural

Important Notes

  • State is temporary and cleared automatically after ~20-30 seconds
  • Recommended to send actual message within 5-10 seconds of setting state
  • Only supported on WWebJS driver (Cloud API does not support presence simulation)

Use Cases

Use Case Endpoint Description
Dashboard Inbox GET /chats?unreadOnly=true Display only chats with pending messages
Group Management GET /chats?groups=true List all group conversations
Notification Control POST /chats/:id/mute Silence noisy chats during business hours
Priority Chats POST /chats/:id/pin Keep important customers at the top
Archive Management POST /chats/:id/archive Hide completed support tickets
âš ī¸ Important Notes:
  • WWebJS Only: Chat Management is not available with Cloud API driver (Meta API limitation)
  • Session State: All endpoints require session in READY state
  • Chat ID Format: Use WhatsApp format (e.g., 5511999999999@c.us for individuals, xxxxx@g.us for groups)
  • Client-Side Filtering: Filters are applied after fetching all chats (performance consideration for large chat lists)
  • Mute Duration: Omit duration parameter for indefinite mute

đŸ‘Ĩ Group Management

✨ NEW in v2.89

Manage WhatsApp groups with create, update, participant management, and administrative operations. Both WWebJS and Cloud API drivers supported.

â„šī¸ Group Management Features:
  • Create Groups: Both drivers (Cloud API limit: 8 participants, WWebJS: unlimited)
  • Get Group Info: Retrieve group details, participants, and admins
  • Add/Remove Participants: Manage group membership (batch operations supported)
  • Update Group: Change group name and description
  • Leave Group: Exit from a group programmatically
  • Promote/Demote Admins: âš ī¸ WWebJS driver only
  • Invite Code Management: âš ī¸ WWebJS driver only
  • Group Settings: âš ī¸ WWebJS driver only
âš ī¸ Driver-Specific Limitations:
  • Cloud API - Participant Limit: Maximum 8 participants during group creation (WhatsApp Business API restriction)
  • Cloud API - Admin Operations Not Supported: promote/demote, invite code retrieval/revoke, group settings
  • WWebJS - No Limitations: Supports all group operations including admin management
  • Error Handling: Unsupported operations return HTTP 400 with DriverNotSupportedError

POST /api/v1/groups

Create a new WhatsApp group with participants. Requires at least 1 participant. Returns group ID and participant count.

Request Body

Field Type Required Description
sessionId string Yes Session ID to use for group creation
name string Yes Group name (max 25 characters - WhatsApp limit)
description string No Group description (max 512 characters - WhatsApp limit)
participants string[] Yes Array of participant phone numbers with country code (Cloud API max 8, WWebJS unlimited)

Example Request

curl -X POST https://apiwts.top/api/v1/groups \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession",
    "name": "Team Project Alpha",
    "description": "Project discussion and updates",
    "participants": [
      "5511999999999",
      "5511988888888",
      "5511977777777"
    ]
  }'

Example Response

{
  "success": true,
  "groupId": "123456789012345678@g.us",
  "inviteCode": "AbCdEfGh1234567", // WWebJS only
  "participantsAdded": 3
}

GET /api/v1/groups

WWebJS Only List all groups for a session. This is a convenience wrapper for GET /api/v1/chats with groups filter. Returns a simplified list of groups with basic information.

Query Parameters

Parameter Type Required Description
sessionId string Yes Session ID to use
limit number No Maximum number of groups to return (default: 100, max: 500)

Example Request

curl -X GET "https://apiwts.top/api/v1/groups?sessionId=mysession&limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "groups": [
    {
      "id": "123456789012345678@g.us",
      "name": "Team Project Alpha",
      "participantCount": 8,
      "isGroup": true
    },
    {
      "id": "987654321098765432@g.us",
      "name": "Customer Support",
      "participantCount": 15,
      "isGroup": true
    }
  ],
  "total": 2
}

Error Responses v2.92+

Status Error Description
400 Bad Request Session is not in READY state, or using Cloud API driver (not supported)
401 Unauthorized Missing or invalid API key
404 Not Found Session not found or driver not initialized
503 Service Unavailable WhatsApp client not ready (try again later)

Error Response Example

// 404 - Session not found
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session mysession not found"
}

// 400 - Session not ready
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Session mysession is not ready (current state: DISCONNECTED)"
}

// 503 - Client not initialized
{
  "statusCode": 503,
  "error": "Service Unavailable",
  "message": "WhatsApp client is not ready. Please try again later."
}

GET /api/v1/groups/:groupId

Retrieve detailed information about a specific WhatsApp group including name, description, participants list, and admins.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Query Parameters

Parameter Type Required Description
sessionId string Yes Session ID to use

Example Request

curl -X GET "https://apiwts.top/api/v1/groups/123456789012345678@g.us?sessionId=mysession" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "id": "123456789012345678@g.us",
  "name": "Team Project Alpha",
  "description": "Project discussion and updates",
  "participants": [
    {
      "id": "5511999999999@c.us",
      "isAdmin": true,
      "isSuperAdmin": true
    },
    {
      "id": "5511988888888@c.us",
      "isAdmin": false,
      "isSuperAdmin": false
    },
    {
      "id": "5511977777777@c.us",
      "isAdmin": true,
      "isSuperAdmin": false
    }
  ],
  "admins": [
    "5511999999999@c.us",
    "5511977777777@c.us"
  ],
  "createdAt": "2025-01-15T10:30:00Z"
}

POST /api/v1/groups/:groupId/participants

Add one or more participants to a WhatsApp group. Returns results array showing success/failure for each participant.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use
participants string[] Yes Array of participant phone numbers to add

Example Request

curl -X POST https://apiwts.top/api/v1/groups/123456789012345678@g.us/participants \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession",
    "participants": [
      "5511966666666",
      "5511955555555"
    ]
  }'

Example Response

{
  "success": true,
  "results": [
    {
      "participant": "5511966666666@c.us",
      "success": true
    },
    {
      "participant": "5511955555555@c.us",
      "success": false,
      "error": "Participant has privacy settings restricting group invites"
    }
  ]
}

DELETE /api/v1/groups/:groupId/participants

Remove one or more participants from a WhatsApp group. Requires admin permissions. Returns results array showing success/failure for each participant.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use
participants string[] Yes Array of participant phone numbers to remove

Example Request

curl -X DELETE https://apiwts.top/api/v1/groups/123456789012345678@g.us/participants \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession",
    "participants": [
      "5511966666666"
    ]
  }'

Example Response

{
  "success": true,
  "results": [
    {
      "participant": "5511966666666@c.us",
      "success": true
    }
  ]
}

PATCH /api/v1/groups/:groupId

Update group name and/or description. Requires admin permissions. At least one field (name or description) must be provided.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use
name string No New group name (max 25 characters)
description string No New group description (max 512 characters)

Example Request

curl -X PATCH https://apiwts.top/api/v1/groups/123456789012345678@g.us \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession",
    "name": "Team Alpha - Q1 2025",
    "description": "Updated project scope for Q1"
  }'

Example Response

{
  "success": true
}

POST /api/v1/groups/:groupId/leave

Leave a WhatsApp group. This action removes the session user from the group and cannot be undone programmatically.

Path Parameters

Parameter Type Description
groupId string Group ID to leave (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use

Example Request

curl -X POST https://apiwts.top/api/v1/groups/123456789012345678@g.us/leave \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession"
  }'

Example Response

{
  "success": true
}

POST /api/v1/groups/:groupId/participants/promote

âš ī¸ WWebJS Only Promote one or more participants to group admin. Requires admin permissions. Returns results array showing success/failure for each participant.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use (WWebJS driver required)
participants string[] Yes Array of participant phone numbers to promote

Example Request

curl -X POST https://apiwts.top/api/v1/groups/123456789012345678@g.us/participants/promote \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession",
    "participants": [
      "5511966666666"
    ]
  }'

Example Response (WWebJS)

{
  "success": true,
  "results": [
    {
      "participant": "5511966666666@c.us",
      "success": true
    }
  ]
}

Example Response (Cloud API - Not Supported)

{
  "statusCode": 400,
  "error": "Driver Not Supported",
  "message": "Feature \"promoteParticipants\" is not supported by Cloud API driver",
  "code": "DRIVER_NOT_SUPPORTED",
  "feature": "promoteParticipants",
  "driver": "Cloud API"
}

POST /api/v1/groups/:groupId/participants/demote

âš ī¸ WWebJS Only Demote one or more group admins to regular participants. Requires admin permissions. Returns results array showing success/failure for each participant.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use (WWebJS driver required)
participants string[] Yes Array of participant phone numbers to demote

Example Request

curl -X POST https://apiwts.top/api/v1/groups/123456789012345678@g.us/participants/demote \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession",
    "participants": [
      "5511977777777"
    ]
  }'

Example Response

{
  "success": true,
  "results": [
    {
      "participant": "5511977777777@c.us",
      "success": true
    }
  ]
}

GET /api/v1/groups/:groupId/invite-code

âš ī¸ WWebJS Only Retrieve the group's invite code (invitation link). Returns a 16-character code that can be used to join the group.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Query Parameters

Parameter Type Required Description
sessionId string Yes Session ID to use (WWebJS driver required)

Example Request

curl -X GET "https://apiwts.top/api/v1/groups/123456789012345678@g.us/invite-code?sessionId=mysession" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "inviteCode": "AbCdEfGh1234567",
  "inviteLink": "https://chat.whatsapp.com/AbCdEfGh1234567"
}

POST /api/v1/groups/:groupId/invite-code/revoke

âš ī¸ WWebJS Only Revoke the current invite code and generate a new one. Previous invite links will no longer work. Returns the new invite code.

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use (WWebJS driver required)

Example Request

curl -X POST https://apiwts.top/api/v1/groups/123456789012345678@g.us/invite-code/revoke \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession"
  }'

Example Response

{
  "success": true,
  "newInviteCode": "XyZ9876543210Ab"
}

PATCH /api/v1/groups/:groupId/settings

âš ī¸ WWebJS Only Update group settings for participant restrictions (who can send messages, edit group info).

Path Parameters

Parameter Type Description
groupId string Group ID (format: 123456789-1234567890@g.us)

Request Body

Field Type Required Description
sessionId string Yes Session ID to use (WWebJS driver required)
messageSend boolean No If false, only admins can send messages
groupInfoEdit boolean No If false, only admins can edit group info

Example Request

curl -X PATCH https://apiwts.top/api/v1/groups/123456789012345678@g.us/settings \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "mysession",
    "messageSend": false,
    "groupInfoEdit": false
  }'

Example Response

{
  "success": true
}
âš ī¸ Important Notes:
  • Group ID Format: Use WhatsApp format (e.g., 123456789-1234567890@g.us)
  • Admin Permissions Required: Most operations (add/remove/promote/demote/settings) require the session user to be a group admin
  • Cloud API Participant Limit: Maximum 8 participants during group creation (WhatsApp Business API restriction)
  • WWebJS-Only Operations: promote/demote, invite code management, and group settings return HTTP 400 with DriverNotSupportedError on Cloud API
  • Batch Operations: add/remove/promote/demote support multiple participants in a single request
  • Character Limits: Group name (25 chars), description (512 chars) enforced by WhatsApp
  • Leave Action Permanent: Leaving a group cannot be undone via API (requires manual re-invite)

📸 Instagram Messaging v3.10.0

Send and receive Instagram Direct Messages via Meta's Graph API. Available as a separate driver for Instagram Business accounts.

â„šī¸ About Instagram Messaging API:
  • Driver: Uses Meta Graph API (separate from WhatsApp Cloud API)
  • Requirements: Instagram Business Account + Facebook Page + Meta App
  • Authentication: OAuth 2.0 with long-lived tokens (60 days, auto-refresh)
  • Webhooks: Receive DMs via webhook subscriptions
  • 24-Hour Window: Must respond within 24 hours of user's last message
âš ī¸ Prerequisites:
  • Instagram Business Account: Personal accounts are NOT supported
  • Connected Facebook Page: Page must be linked to Instagram account
  • Meta App: Create app at developers.facebook.com
  • Permissions: instagram_basic, instagram_manage_messages, pages_manage_metadata, pages_messaging
  • App Review: Required for production use (Meta approval process)

Environment Configuration

Set these environment variables in your deployment:

# Meta App Configuration (Global - shared across all tenants)
META_APP_ID=your_meta_app_id
META_APP_SECRET=your_meta_app_secret
GRAPH_API_VERSION=v24.0
INSTAGRAM_WEBHOOK_VERIFY_TOKEN=your_secure_random_string

OAuth Flow (Admin Only)

Connect an Instagram Business account to your tenant:

Step 1: Start OAuth

GET /admin/api/instagram/oauth/start

// Response:
{
  "authUrl": "https://www.facebook.com/v24.0/dialog/oauth?..."
}

Redirect admin user to authUrl to grant permissions.

Step 2: OAuth Callback

GET /admin/api/instagram/oauth/callback?code=xxx&state=yyy

// Response (on success):
{
  "success": true,
  "igUserId": "17841401234567890",
  "igUsername": "mybusiness",
  "pageId": "123456789012345",
  "pageName": "My Business Page"
}

Sending Messages

Once connected, use the standard messaging endpoints with the Instagram session:

Send Text Message

POST /api/v1/messages/text
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "sessionId": "instagram-mybusiness",
  "to": "17841409876543210",  // Instagram-scoped ID (IGSID)
  "message": "Hello from Instagram!"
}

Send Media

POST /api/v1/messages/media
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "sessionId": "instagram-mybusiness",
  "to": "17841409876543210",
  "mediaUrl": "https://example.com/image.jpg",
  "type": "image",
  "caption": "Check out this product!"
}

Send Quick Replies

POST /api/v1/messages/interactive
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "sessionId": "instagram-mybusiness",
  "to": "17841409876543210",
  "type": "quick_replies",
  "body": "How can we help you today?",
  "quickReplies": [
    {"id": "order_status", "title": "Order Status"},
    {"id": "returns", "title": "Returns"},
    {"id": "support", "title": "Talk to Support"}
  ]
}

Receiving Messages (Webhooks)

Configure webhook URL in your Meta App dashboard:

Webhook URL: https://your-domain.com/webhooks/instagram
Verify Token: (same as INSTAGRAM_WEBHOOK_VERIFY_TOKEN env var)

Webhook Payload (message.received)

{
  "event": "message.received",
  "timestamp": 1704067200000,
  "sessionId": "instagram-mybusiness",
  "tenantId": "your-tenant-id",
  "data": {
    "from": "17841409876543210",
    "to": "17841401234567890",
    "body": "Hi, I have a question about my order",
    "type": "text",
    "messageId": "aWdfZAG1faW...",
    "metadata": {
      "platform": "instagram",
      "timestamp": "2024-01-01T12:00:00.000Z"
    }
  }
}

Instagram-Specific Limitations

âš ī¸ Key Differences from WhatsApp:
  • 24-Hour Messaging Window: Can only send messages within 24 hours of user's last message (standard messaging). Human Agent tag extends to 7 days.
  • No Phone Numbers: Uses Instagram-Scoped IDs (IGSID) instead of phone numbers
  • No Groups: Instagram DMs are 1:1 only (group chats not supported via API)
  • No Templates: Template messages not available (use WhatsApp Cloud API for templates)
  • Media Limits: Images max 8MB, Videos max 25MB
  • Rate Limits: Subject to Meta Graph API rate limiting

Supported Message Types

Type Support Notes
Text ✅ Plain text messages
Image ✅ JPEG, PNG (max 8MB)
Video ✅ MP4 (max 25MB, 60 seconds)
Audio ✅ Voice messages
Sticker ✅ Static and animated
Quick Replies ✅ Up to 13 buttons
Generic Template ✅ Carousel with cards
Ice Breakers ✅ Conversation starters (FAQ)

Token Management

The Instagram driver automatically manages token lifecycle:

  • User Token: Long-lived (60 days), stored encrypted in database
  • Page Token: Derived from user token, never expires while user token valid
  • Auto-Refresh: Tokens refreshed 10 days before expiration
  • Encryption: AES-256-GCM encryption for stored tokens
â„šī¸ Token Refresh Process:

The system automatically refreshes tokens before expiration. If refresh fails (e.g., user revoked permissions), the session will be marked as token_expired and admin will need to re-authenticate via OAuth flow.

Advanced Features v3.10.0+

Instagram-specific advanced features for enhanced messaging capabilities.

Sender Actions (Typing Indicators)

Show typing indicators or mark messages as seen:

POST /api/v1/instagram/sessions/:sessionId/typing
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "recipientId": "17841409876543210",
  "action": "typing_on"  // typing_on | typing_off | mark_seen
}

// Response:
{
  "success": true,
  "action": "typing_on",
  "recipientId": "17841409876543210"
}
â„šī¸ Sender Action Types:
  • typing_on - Shows typing indicator (auto-clears after 20s or next message)
  • typing_off - Hides typing indicator immediately
  • mark_seen - Marks conversation as read (blue checkmarks)

Message Reactions

React to messages with emoji or remove reactions:

// Add reaction
POST /api/v1/instagram/sessions/:sessionId/messages/:messageId/react
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "recipientId": "17841409876543210",
  "reaction": "love"  // love | haha | wow | sad | angry | like | or any emoji
}

// Response:
{
  "success": true,
  "messageId": "aWdfZAG1faW...",
  "reaction": "love"
}

// Remove reaction
DELETE /api/v1/instagram/sessions/:sessionId/messages/:messageId/react
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "recipientId": "17841409876543210"
}

// Response:
{
  "success": true,
  "messageId": "aWdfZAG1faW...",
  "reaction": null
}

Multi-Image Sending (BETA)

Send multiple images in a single message:

POST /api/v1/instagram/sessions/:sessionId/messages/images
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "recipientId": "17841409876543210",
  "imageUrls": [
    "https://example.com/image1.jpg",
    "https://example.com/image2.jpg",
    "https://example.com/image3.jpg"
  ]
}

// Response:
{
  "success": true,
  "messageId": "aWdfZAG1faW...",
  "status": "sent",
  "imagesCount": 3
}
âš ī¸ Multi-Image Limitations (BETA):
  • Maximum: 10 images per message
  • Size Limit: Each image max 8MB
  • Beta Feature: May not be available for all Instagram accounts
  • Error Code: Error subcode 2534068 means feature not enabled for your app

User Moderation

Block, unblock, or mark users as spam:

// Block user
POST /api/v1/instagram/sessions/:sessionId/users/:userId/block
Authorization: Bearer your_api_key

// Response:
{
  "success": true,
  "userId": "17841409876543210",
  "action": "block_user"
}

// Unblock user
DELETE /api/v1/instagram/sessions/:sessionId/users/:userId/block
Authorization: Bearer your_api_key

// Response:
{
  "success": true,
  "userId": "17841409876543210",
  "action": "unblock_user"
}

// Move to spam
POST /api/v1/instagram/sessions/:sessionId/users/:userId/spam
Authorization: Bearer your_api_key

// Response:
{
  "success": true,
  "userId": "17841409876543210",
  "action": "move_to_spam"
}

Instagram Webhook Events v3.10.0+

New webhook events for Instagram messaging:

// Reaction received webhook
{
  "event": "message.reaction",
  "timestamp": 1704067200000,
  "sessionId": "instagram-mybusiness",
  "tenantId": "your-tenant-id",
  "data": {
    "messageId": "aWdfZAG1faW...",
    "from": "17841409876543210",
    "reaction": "â¤ī¸",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

// Message edited webhook
{
  "event": "message.edit",
  "timestamp": 1704067200000,
  "sessionId": "instagram-mybusiness",
  "tenantId": "your-tenant-id",
  "data": {
    "messageId": "aWdfZAG1faW...",
    "from": "17841409876543210",
    "newBody": "Updated message text",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

Feature Flags

Instagram v3.10.0 features can be enabled/disabled via Redis-based feature flags:

Flag Default Description
INSTAGRAM_SENDER_ACTIONS_ENABLED ✅ typing_on/typing_off/mark_seen
INSTAGRAM_REACTIONS_ENABLED ✅ React/unreact to messages
INSTAGRAM_REACTION_WEBHOOKS_ENABLED ✅ Dispatch reaction webhooks
INSTAGRAM_REPLY_TO_ENABLED ✅ Reply-to message parameter
INSTAGRAM_MULTI_IMAGE_ENABLED ✅ Multi-image sending (BETA)
INSTAGRAM_MODERATION_ENABLED ✅ Block/unblock/spam users
INSTAGRAM_MESSAGE_EDIT_WEBHOOKS_ENABLED ✅ Message edit webhooks

📋 Message Templates Cloud API Only

Send pre-approved WhatsApp template messages with variable substitution, rich media, and interactive buttons.

Driver Limitation: Template messages require the Cloud API driver. They are not available with WWebJS.
  • Pre-Approval Required: Templates must be created and approved in Facebook Business Manager
  • Cannot Create via API: Templates are managed in Business Manager UI, not via API
  • Session State: Session must be READY with Cloud API driver configured
â„šī¸ Template Message Capabilities:
  • Components: Header (text/media), Body (with variables), Footer, Buttons (quick reply/call-to-action)
  • Variable Substitution: Replace {{1}}, {{2}} placeholders at send-time for personalization
  • Localization: Support for 60+ languages with language code specification
  • Use Cases: Transactional notifications, marketing campaigns, OTP/authentication, customer support
  • Quality Ratings: Template performance affects delivery limits (GREEN/YELLOW/RED ratings)

How to Create Templates

  1. Access Business Manager: Go to Facebook Business Manager
  2. Navigate to Templates: WhatsApp Manager → Message Templates
  3. Create Template: Click "Create Template" and configure components:
    • Name: Lowercase, underscores, no spaces (e.g., order_confirmation)
    • Category: Marketing, Utility, or Authentication
    • Language: Select primary language (can add more later)
    • Header: Optional text, image, video, document, or location
    • Body: Message text with {{1}}, {{2}} variables (up to 1024 characters)
    • Footer: Optional footer text (up to 60 characters)
    • Buttons: Up to 3 buttons (Call to Action or Quick Reply)
  4. Submit for Review: Meta reviews templates (24-48 hours approval time)
  5. Wait for Approval: Template status: PENDING → APPROVED/REJECTED
  6. Use Approved Template: Only APPROVED templates can be sent via API

POST /api/v1/messages/template

Send a pre-approved WhatsApp template message with variable substitution.

Request Body

Field Type Required Description
sessionId string Yes Session identifier (must be Cloud API session in READY state)
to string Yes Recipient WhatsApp number in E.164 format with country code (e.g., +5511999999999, +41764791479, +14155552671)
templateName string Yes Template name from Business Manager (1-512 characters)
languageCode string No Template language code (e.g., 'en', 'pt_BR', 'es'). Default: 'en'
headerVariables array No Header component variables (for media headers). Array of {type, value}
bodyVariables string[] No Body variables to replace {{1}}, {{2}} placeholders
buttons array No Button component values. Array of {type: 'url'|'quick_reply', value}

Example: Simple Text Template

const response = await fetch('https://apiwts.top/api/v1/messages/template', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'cloud-session-001',
    to: '+5511999999999',
    templateName: 'welcome_message',
    languageCode: 'pt_BR',
    bodyVariables: ['JoÃŖo Silva']
  })
});

const result = await response.json();
console.log(result);
// {
//   "id": "msg_uuid_12345",
//   "sessionId": "cloud-session-001",
//   "to": "+5511999999999",
//   "status": "sent",
//   "type": "template",
//   "timestamp": "2025-10-03T12:00:00.000Z",
//   "driverMessageId": "wamid.HBgLNTU1MTEOTk5OTk5OTk5..."
// }

Example: Order Confirmation Template

// Template in Business Manager:
// Name: order_confirmation
// Body: Hi {{1}}, your order #{{2}} has been confirmed for ${{3}}.
// Footer: Thank you for shopping with us!
// Button: View Order (URL)

const response = await fetch('https://apiwts.top/api/v1/messages/template', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'cloud-session-001',
    to: '+5511999999999',
    templateName: 'order_confirmation',
    languageCode: 'en',
    bodyVariables: ['JoÃŖo Silva', 'ORD-12345', '49.99'],
    buttons: [
      { type: 'url', value: 'https://example.com/orders/12345' }
    ]
  })
});

const result = await response.json();

Example: Template with Image Header

// Template with image header
const response = await fetch('https://apiwts.top/api/v1/messages/template', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'cloud-session-001',
    to: '+5511999999999',
    templateName: 'product_launch',
    languageCode: 'en',
    headerVariables: [
      {
        type: 'image',
        value: 'https://example.com/images/product.jpg'
      }
    ],
    bodyVariables: ['New Product', '25% OFF'],
    buttons: [
      { type: 'url', value: 'https://example.com/products/new' },
      { type: 'quick_reply', value: 'LEARN_MORE' }
    ]
  })
});

Response Schema

Field Type Description
id string Internal message ID (UUID)
sessionId string Session identifier used for sending
to string Recipient WhatsApp number
status string Message status: 'sent', 'queued', 'delivered', etc.
type string Message type: 'template'
timestamp string ISO 8601 timestamp of message creation
driverMessageId string WhatsApp message ID from Cloud API (e.g., wamid.HBgL...)

Error Responses

Status Error Description
400 Bad Request WWebJS driver attempted (Cloud API only feature)
400 Bad Request Session not in READY state
404 Not Found Session not found or doesn't belong to tenant
500 Internal Server Error Template not approved, invalid parameters, or Cloud API error

Common Template Errors

// Error: WWebJS driver
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Template messages are only available for Cloud API driver (WWebJS does not support templates)"
}

// Error: Session not ready
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Session cloud-session-001 is not ready (current state: INITIALIZING)"
}

// Error: Template not approved
{
  "statusCode": 500,
  "error": "Internal Server Error",
  "message": "Failed to send template: Template not found or not approved"
}

// Error: Invalid language code
{
  "statusCode": 500,
  "error": "Internal Server Error",
  "message": "Failed to send template: Language code not supported for template"
}

Template Best Practices

  • Use Descriptive Names: e.g., order_confirmation, password_reset, welcome_message
  • Keep Body Concise: Max 1024 characters, avoid overly long messages
  • Test Before Launch: Send test messages to verify variables and formatting
  • Follow WhatsApp Guidelines: Avoid spam, excessive marketing, or misleading content
  • Monitor Quality Ratings: Poor user feedback affects delivery limits (GREEN → YELLOW → RED)
  • Localize Templates: Create language-specific versions for better engagement
  • Limit Variables: Use only necessary variables to reduce complexity
  • Button Clarity: Use clear call-to-action text (e.g., "View Order" not "Click Here")

Template Categories

Category Use Case Examples
Utility Transactional notifications Order confirmations, shipping updates, appointment reminders, account alerts
Authentication Security and verification OTP codes, password resets, 2FA verification, login alerts
Marketing Promotional messages Product launches, seasonal sales, discount codes, event invitations
âš ī¸ Marketing Template Restrictions:
  • US Phone Numbers: Marketing templates to US numbers paused starting April 1, 2025
  • Rate Limits: Per-user marketing message limits based on account tier and quality rating
  • Opt-Out: Users can block or report marketing messages, affecting quality rating
  • Compliance: Must comply with WhatsApp Business Policy and local regulations (GDPR, CAN-SPAM, etc.)

Driver Compatibility

Feature WWebJS Cloud API
Template Messages ❌ Not Supported ✅ Full Support
Dynamic Templates ❌ Not Supported ❌ Pre-approval Required
Variable Substitution ❌ Not Supported ✅ Full Support
Interactive Buttons ❌ Deprecated ✅ Full Support
Media Headers ❌ Not Supported ✅ Image/Video/Document

Related Resources

🤖 MCP Reference (Model Context Protocol)

✅ PRODUCTION READY 🤖 AI ENABLED 57 TOOLS

The WhatsApp Multi-Driver API exposes all its functionality via Model Context Protocol (MCP), enabling AI agents like Claude Code to interact with WhatsApp using natural language.

What is MCP?

MCP is an open protocol by Anthropic that connects AI applications to external tools and data sources in a standardized way.

đŸŽ¯ Analogy: MCP is like USB-C for AI - a universal connector that allows Claude Code (and other agents) to interact with APIs in a standardized way.

How It Works

Claude Code
    ↓ JSON-RPC 2.0
MCP Server (https://apiwts.top/mcp)
    ↓ Internal API calls
WhatsApp Multi-Driver API
    ↓ Drivers
WhatsApp (WWebJS or Cloud API)

MCP Endpoint

POST /mcp

JSON-RPC 2.0 endpoint for AI tool integration.

// List available tools
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "result": {
    "tools": [
      {
        "name": "sendTextMessage",
        "description": "Send a text message via WhatsApp",
        "inputSchema": {
          "type": "object",
          "properties": {
            "sessionId": { "type": "string" },
            "to": { "type": "string" },
            "text": { "type": "string" }
          },
          "required": ["sessionId", "to", "text"]
        }
      }
      // ... 57 tools
    ]
  },
  "id": 1
}

MCP Lifecycle Methods v3.7.6+

MCP defines lifecycle methods for protocol handshake and health checking. All methods require Bearer token authentication.

initialize

Handshake method to establish protocol version and capabilities. Required as the first method call.

// Request
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "initialize",
  "id": 1,
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": {"name": "my-client", "version": "1.0"}
  }
}

// Response
{
  "jsonrpc": "2.0",
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {"tools": {}},
    "serverInfo": {
      "name": "whatsapp-multi-driver-api",
      "version": "3.7.6"
    },
    "instructions": "WhatsApp Multi-Driver API MCP Server. Use tools/list to discover available operations."
  },
  "id": 1
}
Supported Protocol Versions: 2024-11-05, 2025-03-26

notifications/initialized

Notification sent by client after receiving initialize response. No response expected (JSON-RPC notification).

// Request (notification - no id field)
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

// Response: HTTP 204 No Content (empty body)

ping

Simple health check method.

// Request
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "ping",
  "id": 2
}

// Response
{
  "jsonrpc": "2.0",
  "result": {},
  "id": 2
}

Authentication

MCP uses the same API key authentication as the REST API, but via Bearer token:

// Include API Key in Authorization header
Authorization: Bearer YOUR_API_KEY

46 Available Tools

Sessions Management (9 tools)

Tool Description
createSession Create new WhatsApp session
listSessions List all sessions
getSession Get session details
getSessionStatus Get status + QR availability
getQRCode Get QR code (PNG base64)
connectSession Initialize session
restartSession Restart session
deleteSession Delete session
getSessionHealth Health metrics

Messaging (20 tools)

Tool Description
sendTextMessage Send text message
sendMediaMessage Send media (image/video/document)
sendTemplateMessage Send template (Cloud API only)
sendLocationMessage Send location with coordinates (WWebJS + Cloud API) v3.4+
getMessage Get message status
listMessages List messages
revokeMessage Revoke/delete a sent message (WWebJS only) v2.84+
reactToMessage React to a message with emoji (WWebJS only) v2.85+
getMessageInfo Get detailed delivery info (ACK level, group read receipts) v2.85+
forwardMessage Forward message to another chat (WWebJS only) v2.85+
editMessage Edit a sent text message (~15 min window, WWebJS only) v3.4+
downloadMedia Download media (image/video/audio/doc) from message as base64 (WWebJS only) v3.4+
getQuotedMessage Get the original message that was quoted/replied to (WWebJS only) v3.4+
starMessage Star (favorite) a message (WWebJS only) v3.4+
unstarMessage Remove star (unfavorite) from a message (WWebJS only) v3.4+
getMessageReactions Get all emoji reactions on a message (WWebJS only) v3.4+
pinMessage Pin a message in chat for a specified duration (WWebJS only) v3.4+
unpinMessage Unpin a pinned message in chat (WWebJS only) v3.4+
getMessageMentions Get @mentions in a message - returns list of mentioned contacts (WWebJS only) v3.4+
getPollVotes Get votes from a poll message - returns list of voters with selected options (WWebJS only) v3.4+

Scheduled Messages (4 tools)

Tool Description
scheduleMessage Schedule future message
listScheduledMessages List scheduled messages
cancelScheduledMessage Cancel pending message
getScheduledMessageStats Get statistics

Webhooks (6 tools)

Tool Description
createWebhook Configure webhook URL
listWebhooks List webhooks
updateWebhook Update webhook
deleteWebhook Delete webhook
listWebhookDeliveries Delivery history
testWebhook Test webhook

Contacts (3 tools - WWebJS only)

Tool Description
listContacts List contacts
blockContact Block contact
unblockContact Unblock contact

Chats (6 tools - WWebJS only)

Tool Description
listChats List conversations
archiveChat Archive conversation
unarchiveChat Unarchive conversation
muteChat Mute conversation
pinChat Pin conversation
setChatState Set chat state - typing/recording indicators (WWebJS only) v2.85+

Groups (12 tools)

Tool Description Driver
createGroup Create new group Both
getGroupInfo Get group details Both
listGroups List all groups WWebJS
addGroupParticipants Add members to group Both
removeGroupParticipants Remove members from group Both
updateGroup Update group name/description Both
promoteGroupAdmin Promote member to admin WWebJS
demoteGroupAdmin Demote admin to member WWebJS
updateGroupSettings Update group settings WWebJS
getGroupInviteCode Get group invite link WWebJS
revokeGroupInvite Revoke invite link WWebJS
leaveGroup Leave a group Both

Monitoring (2 tools)

Tool Description
getHealthStatus System health status
getMetrics Prometheus metrics

Usage Examples

Example 1: Tool Discovery

// Request: List all available tools
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "result": {
    "tools": [...49 tools...]
  },
  "id": 1
}

Example 2: Call a Tool

// Request: Get health status
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "getHealthStatus",
    "arguments": {}
  },
  "id": 2
}

// Response
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"status\":\"healthy\",\"database\":\"connected\",\"redis\":\"connected\"}"
      }
    ]
  },
  "id": 2
}

Example 3: Send Message via Tool

// Request: Send WhatsApp message
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "sendTextMessage",
    "arguments": {
      "sessionId": "my-session",
      "to": "+5511999999999",
      "text": "Hello from MCP!"
    }
  },
  "id": 3
}

// Response
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"id\":\"msg_123\",\"status\":\"queued\",\"sessionId\":\"my-session\"}"
      }
    ]
  },
  "id": 3
}

Example 4: Complete Workflow

// Step 1: Create session
tools/call → createSession({ sessionId: "support" })

// Step 2: Connect session (generate QR)
tools/call → connectSession({ sessionId: "support" })

// Step 3: Get QR code
tools/call → getQRCode({ sessionId: "support" })
// Returns: { qr: "data:image/png;base64,..." }

// Step 4: Wait for session ready (webhook or polling)

// Step 5: Send message
tools/call → sendTextMessage({
  sessionId: "support",
  to: "+5511999999999",
  text: "Hello from MCP Server!"
})

Example 5: Revoke Message (v2.84+)

// Request: Delete message for everyone
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "revokeMessage",
    "arguments": {
      "messageId": "550e8400-e29b-41d4-a716-446655440000",
      "revokeType": "everyone"
    }
  },
  "id": 5
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Message revoked successfully (everyone)\",\"revokedAt\":\"2025-11-23T06:00:00.000Z\",\"revokeType\":\"everyone\"}"
      }
    ]
  },
  "id": 5
}

// Natural language usage with Claude Code
"Delete the message with ID 550e8400-e29b-41d4-a716-446655440000 for everyone"
"Revoke my last sent message"
"Delete message abc-123 only for me"

// Important notes
- Only supported on WWebJS driver (Cloud API will return error)
- Messages can be deleted for everyone within ~48 hours
- Requires message status: sent, delivered, or read
- revokeType: "me" (delete for yourself) or "everyone" (default)

Example 6: React to Message (v2.85+)

// Request: Add emoji reaction to a message
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "reactToMessage",
    "arguments": {
      "messageId": "550e8400-e29b-41d4-a716-446655440000",
      "emoji": "👍"
    }
  },
  "id": 6
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Reaction \\\"👍\\\" added successfully\",\"data\":{\"messageId\":\"550e8400-e29b-41d4-a716-446655440000\",\"emoji\":\"👍\",\"reactedAt\":\"2025-11-23T08:00:00.000Z\"}}"
      }
    ]
  },
  "id": 6
}

// Natural language usage with Claude Code
"React to message 550e8400 with thumbs up emoji"
"Add heart reaction â¤ī¸ to the last message"
"React with 😂 to message abc-123"

// Important notes
- Only supported on WWebJS driver (Cloud API will return error)
- Common emojis: 👍, â¤ī¸, 😂, 😮, đŸ˜ĸ, 🙏
- Message must have status: sent, delivered, or read

Example 7: Get Message Delivery Info (v2.85+)

// Request: Get detailed delivery information
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "getMessageInfo",
    "arguments": {
      "messageId": "550e8400-e29b-41d4-a716-446655440000"
    }
  },
  "id": 7
}

// Response (Success - Individual Chat)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"data\":{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"ack\":3,\"timestamp\":\"2025-11-23T08:00:00.000Z\"}}"
      }
    ]
  },
  "id": 7
}

// Natural language usage with Claude Code
"Get delivery info for message 550e8400"
"Check if my last message was read"
"Show delivery details for message abc-123"

// ACK Levels
- -1: Error (message failed)
- 0: Pending (queued)
- 1: Server (sent to WhatsApp)
- 2: Device (delivered to recipient)
- 3: Read (read by recipient)
- 4: Played (voice/video played)

// Important notes
- Only supported on WWebJS driver
- Group chats include deliveredTo, readBy, playedBy arrays

Example 8: Forward Message (v2.85+)

// Request: Forward message to another chat
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "forwardMessage",
    "arguments": {
      "messageId": "550e8400-e29b-41d4-a716-446655440000",
      "targetChatId": "5511888888888@c.us"
    }
  },
  "id": 8
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Message forwarded successfully\",\"data\":{\"messageId\":\"550e8400-e29b-41d4-a716-446655440000\",\"targetChatId\":\"5511888888888@c.us\",\"forwardedAt\":\"2025-11-23T08:00:00.000Z\"}}"
      }
    ]
  },
  "id": 8
}

// Natural language usage with Claude Code
"Forward message 550e8400 to +5511888888888"
"Forward the last message to chat 5511999999999@c.us"
"Forward message abc-123 to group 120363..."

// Important notes
- Only supported on WWebJS driver (Cloud API will return error)
- Message must have status: sent, delivered, or read
- newDriverMessageId may not be available (WWebJS limitation)

Example 9: Edit Message (v3.4+)

// Request: Edit a sent text message
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "editMessage",
    "arguments": {
      "messageId": "550e8400-e29b-41d4-a716-446655440000",
      "newContent": "Updated message content"
    }
  },
  "id": 9
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Message edited successfully\",\"data\":{\"messageId\":\"550e8400-e29b-41d4-a716-446655440000\",\"newContent\":\"Updated message content\",\"editedAt\":\"2025-11-28T10:00:00.000Z\"}}"
      }
    ]
  },
  "id": 9
}

// Natural language usage with Claude Code
"Edit message 550e8400 to say 'Updated content'"
"Change my last message to 'Fixed typo here'"
"Update the message I sent to 'Correct information'"

// Important notes
- Only supported on WWebJS driver (Cloud API will return error)
- Only text messages can be edited (not media/location/contacts)
- Messages can only be edited within ~15 minutes of sending
- Message must have status: sent, delivered, or read

Example 10: Download Media (v3.4+)

// Request: Download media from a message
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "downloadMedia",
    "arguments": {
      "messageId": "550e8400-e29b-41d4-a716-446655440000"
    }
  },
  "id": 10
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Media downloaded successfully\",\"data\":{\"messageId\":\"550e8400-e29b-41d4-a716-446655440000\",\"base64\":\"/9j/4AAQSkZJRgABAQAAAQABAAD/...\",\"mimetype\":\"image/jpeg\",\"filename\":\"photo.jpg\",\"filesize\":245678}}"
      }
    ]
  },
  "id": 10
}

// Natural language usage with Claude Code
"Download the media from that message"
"Get the image from message 550e8400"
"Save the photo that was sent to me"

// Important notes
- Only supported on WWebJS driver (Cloud API provides URLs in webhooks)
- Message must contain media (image, video, audio, document)
- Returns base64-encoded data with mimetype and optional filename/filesize

Example 11: Send Location (v3.4+)

// Request: Send a location message
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "sendLocationMessage",
    "arguments": {
      "sessionId": "my-session-001",
      "to": "5511999999999",
      "latitude": -23.5505199,
      "longitude": -46.6333094,
      "description": "SÃŖo Paulo, Brazil",
      "address": "Av. Paulista, 1578 - Bela Vista"
    }
  },
  "id": 11
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Location message sent successfully\",\"data\":{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"driverMessageId\":\"true_5511999999999@c.us_ABCDEF123456\",\"sessionId\":\"my-session-001\",\"to\":\"5511999999999\",\"latitude\":-23.5505199,\"longitude\":-46.6333094,\"description\":\"SÃŖo Paulo, Brazil\",\"address\":\"Av. Paulista, 1578 - Bela Vista\",\"status\":\"sent\",\"timestamp\":\"2025-11-28T14:00:00.000Z\"}}"
      }
    ]
  },
  "id": 11
}

// Natural language usage with Claude Code
"Send my location to contact 5511999999999"
"Share the coordinates -23.55, -46.63 with that user"
"Send the restaurant location to the group"

// Important notes
- Supported on WWebJS and Cloud API drivers
- Latitude must be between -90 and 90
- Longitude must be between -180 and 180
- Description and address are optional (max 256 chars each)

Example 12: Get Quoted Message (v3.4+)

// Request: Get the quoted/replied-to message
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "getQuotedMessage",
    "arguments": {
      "messageId": "msg-uuid-of-reply-message"
    }
  },
  "id": 12
}

// Response (Success - message is a reply)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Quoted message retrieved successfully\",\"data\":{\"id\":\"true_5511999999999@c.us_AAABBCCCDDD\",\"from\":\"5511888888888@c.us\",\"to\":\"5511999999999@c.us\",\"body\":\"Original message text here\",\"type\":\"chat\",\"timestamp\":\"2025-11-28T10:30:00.000Z\",\"hasMedia\":false}}"
      }
    ]
  },
  "id": 12
}

// Response (Success - message is NOT a reply)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Message is not a reply\",\"data\":null}"
      }
    ]
  },
  "id": 12
}

// Natural language usage with Claude Code
"Get the original message that was replied to in message xyz"
"What message was this replying to?"
"Show me the quoted message context"

// Important notes
- WWebJS driver only
- Returns null if message is not a reply
- Useful for building conversation threads

Example 13: Star/Unstar Message (v3.4+)

// Request: Star (favorite) a message
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "starMessage",
    "arguments": {
      "messageId": "msg-uuid-here"
    }
  },
  "id": 13
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Message starred successfully\",\"data\":{\"messageId\":\"msg-uuid-here\",\"starredAt\":\"2025-11-29T10:30:00.000Z\"}}"
      }
    ]
  },
  "id": 13
}

// Request: Unstar a message
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "unstarMessage",
    "arguments": {
      "messageId": "msg-uuid-here"
    }
  },
  "id": 14
}

// Natural language usage with Claude Code
"Star this important message for later"
"Favorite message xyz"
"Remove the star from this message"
"Unstar message xyz"

// Important notes
- WWebJS driver only
- Starred messages appear in chat's "Starred Messages" section
- Useful for bookmarking important messages

Example 14: Set Chat State (v2.85+)

// Request: Show typing indicator
POST https://apiwts.top/mcp
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "setChatState",
    "arguments": {
      "sessionId": "my-session-001",
      "chatId": "5511999999999@c.us",
      "state": "typing"
    }
  },
  "id": 9
}

// Response (Success)
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\":true,\"message\":\"Chat state \\\"typing\\\" set successfully\",\"data\":{\"chatId\":\"5511999999999@c.us\",\"state\":\"typing\",\"appliedAt\":\"2025-11-23T08:00:00.000Z\"}}"
      }
    ]
  },
  "id": 9
}

// Natural language usage with Claude Code
"Show typing indicator to +5511999999999"
"Simulate recording state in chat 5511999999999@c.us"
"Stop typing indicator in the current chat"

// States
- typing: Shows "typing..." indicator
- recording: Shows "recording..." indicator (voice messages)
- stop: Clears any active state

// Important notes
- Only supported on WWebJS driver
- State clears automatically after ~20-30 seconds
- Best practice: Send actual message within 5-10 seconds
- Use case: Simulate human-like bot behavior

Integration with Claude Code

To use this MCP server with Claude Code CLI:

# Register MCP server
claude mcp add --transport http whatsapp-api https://apiwts.top/mcp \
  --header "Authorization: Bearer YOUR_API_KEY"

# List available tools
claude mcp list whatsapp-api

# Use via natural language
claude ask "Use whatsapp-api to send message 'Hello!' to +5511999999999 via session 'test'"

Error Handling

MCP follows JSON-RPC 2.0 error format:

// Error response
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32600,
    "message": "Invalid Request",
    "data": {
      "details": "Missing required parameter: sessionId"
    }
  },
  "id": 1
}

// Common error codes
-32700: Parse error
-32600: Invalid Request
-32601: Method not found
-32602: Invalid params
-32603: Internal error

Testing & Debug

MCP Inspector (Official Tool)

npx @modelcontextprotocol/inspector https://apiwts.top/mcp

Visual interface to test tools, see responses, and debug errors.

Manual Testing with cURL

# List tools
curl -X POST https://apiwts.top/mcp \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

# Call tool
curl -X POST https://apiwts.top/mcp \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"getHealthStatus","arguments":{}},"id":2}'
✨ Benefits:
  • Natural Language: Use WhatsApp API via plain English commands
  • No Documentation Required: AI agent knows all endpoints and parameters
  • Autocomplete: Tools and parameters are discoverable
  • Standardized: Works with any MCP-compatible AI agent

Additional Resources

🔧 Troubleshooting & Common Errors

✅ v2.62+ VALIDATION

Error: Missing Required Parameters

JSON-RPC Error Code: -32602

âš ī¸ Common Causes:
  • Typo in parameter name (e.g., scheduledFor instead of scheduledAt)
  • Parameter not sent in request
  • Parameter with value null, undefined, or empty string
  • Using wrong parameter name (e.g., id instead of webhookId)
Example Error Response
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32602,
    "message": "ValidationError"
  },
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{
  \"code\": -32602,
  \"error\": \"ValidationError\",
  \"message\": \"Missing required parameters for scheduleMessage: scheduledAt\",
  \"data\": {
    \"toolName\": \"scheduleMessage\",
    \"received\": [\"sessionId\", \"to\", \"text\", \"scheduledFor\"],
    \"expected\": [\"sessionId\", \"to\", \"text\", \"scheduledAt\"],
    \"missing\": [\"scheduledAt\"],
    \"hint\": \"Did you mean 'scheduledAt' instead of 'scheduledFor'?\"
  }
}"
      }
    ]
  },
  "id": 1
}
How to Fix
  1. Check the error message for the exact parameter name required
  2. Look at the hint field for typo suggestions
  3. Compare received vs expected parameters
  4. Correct the parameter name in your request

Common Typos & Fixes

❌ Wrong (Common Typo) ✅ Correct Tool
scheduledFor scheduledAt scheduleMessage
id webhookId updateWebhook, deleteWebhook, testWebhook
sesionId sessionId All session tools
chatid chatId All chat tools
contactid contactId blockContact, unblockContact

Required Parameters Quick Reference

Tool Required Parameters
sendTextMessagesessionId, to, text
sendMediaMessagesessionId, to, mediaType, mediaUrl
sendTemplateMessagesessionId, to, templateName, language
scheduleMessagesessionId, to, text, scheduledAt
getMessagesessionId, messageId
listMessagessessionId
createSessionsessionId
getSessionsessionId
deleteSessionsessionId
getQRCodesessionId
getSessionStatussessionId
connectSessionsessionId
restartSessionsessionId
getSessionHealthsessionId
createWebhookurl, events
updateWebhookwebhookId
deleteWebhookwebhookId
listWebhookDeliverieswebhookId
testWebhookwebhookId
listContactssessionId
blockContactsessionId, contactId
unblockContactsessionId, contactId
listChatssessionId
archiveChatsessionId, chatId
unarchiveChatsessionId, chatId
muteChatsessionId, chatId
pinChatsessionId, chatId

Validation Features (v2.62+)

  • Typo Detection: Levenshtein distance algorithm suggests correct parameter names
  • Clear Error Messages: Exact parameter names that are missing
  • JSON-RPC 2.0 Compliance: Standard error code -32602 for invalid params
  • Helpful Hints: Automatic suggestions for common typos (edit distance ≤ 2)
💡 Pro Tip: If you receive a validation error, check the hint field first - it often contains the exact fix you need!

⚡ Advanced Features

This section covers advanced configuration and deployment patterns for the WhatsApp API.

Session Reliability

The API includes automatic session recovery features:

  • Auto-Reconnection: Automatic retry on temporary disconnections (3 attempts, 10s interval)
  • Orphan Recovery: Sessions restored automatically on container restart
  • Detached Frame Detection: Automatic recovery from Puppeteer corruption errors
  • Health Monitoring: Continuous health checks with automatic status updates

Blue-Green Deployment

For zero-downtime deployments, the API supports blue-green architecture:

  • Dual Environments: Blue and Green containers with isolated session volumes
  • Instant Rollback: Traffic switch in under 5 seconds via symlink
  • Session Sync: Automatic session synchronization between environments
  • Shared Infrastructure: PostgreSQL and Redis shared between environments

Rate Limiting

Quality-based adaptive rate limiting protects your account:

  • GREEN Rating: Normal sending limits
  • YELLOW Rating: Reduced limits, monitoring required
  • RED Rating: Severely restricted, review account health
  • Recipient Cooldown: 6-10 second delays between messages to same recipient

🚧 Roadmap

Planned features for future releases. These endpoints are documented for reference but are not currently available.

Important: Do not attempt to use these endpoints in production. Check the GitHub repository for implementation updates.

Planned Message Types

POST /api/v1/messages/location

Send location messages with GPS coordinates

Planned
POST /api/v1/messages/contact

Send contact cards (vCard format)

Planned
POST /api/v1/messages/poll

Create interactive polls with multiple options

Planned

Planned Group Features

PUT /api/v1/groups/:groupId/picture

Update group profile picture

Planned
DELETE /api/v1/groups/:groupId/picture

Remove group profile picture

Planned

đŸ› ī¸ Error Handling

HTTP Status Codes

Code Meaning Solution
200 Success Request completed successfully
401 Unauthorized Check API Key - verify in admin panel
404 Not Found Session doesn't exist - create session first
409 Conflict Session already authenticated
429 Rate Limited Too many requests - wait 60 seconds
500 Server Error Try again later - check status page

Error Response Format

{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Invalid API Key",
  "timestamp": "2025-01-15T10:35:00.000Z"
}

Universal Error Handler

const handleApiError = (response) => {
  switch (response.status) {
    case 401:
      return 'Invalid API Key - Check credentials';
    case 404:
      return 'Session not found - Create session first';
    case 409:
      return 'Session already authenticated';
    case 429:
      return 'Rate limit exceeded - Wait 60 seconds';
    case 500:
      return 'Server error - Try again later';
    default:
      return `Unexpected error: ${response.status}`;
  }
};

âš ī¸ Common Pitfalls & Solutions

1. CORS Issues

❌ Problem: "blocked by CORS policy" error
✅ Solution: Use proxy configuration, never direct external URLs in browser
// ❌ WRONG - Direct external URL
fetch('http://external-api.com/endpoint')

// ✅ CORRECT - Relative URL with proxy
fetch('/api/v1/sessions')

2. Phone Number Format

❌ Problem: Message sending fails with invalid number
✅ Solution: Use international format with country code (E.164)
🌍 International Support (v2.29+): The API now supports 200+ country codes automatically. Always include the country code in your phone numbers to ensure proper delivery.
// ✅ CORRECT - International format with country code
const validPhoneNumbers = [
  '+5511999999999',    // Brazil
  '+41764791479',      // Switzerland
  '+14155552671',      // United States
  '+447911123456',     // United Kingdom
  '+8613912345678',    // China
  '+81312345678',      // Japan
  '+491512345678',     // Germany
];

// ❌ WRONG - Missing country code
const invalidPhoneNumbers = [
  '11999999999',       // Ambiguous - which country?
  '7641234567',        // Could be Russia (+7) or Kazakhstan
];

// Phone number formatting helper
const formatPhoneNumber = (phone) => {
  // Remove all non-digit characters
  const cleaned = phone.replace(/\D/g, '');

  // Add + prefix if not present
  return phone.startsWith('+') ? phone : `+${cleaned}`;
};

// Usage examples
const phoneNumber1 = formatPhoneNumber('+5511999999999'); // +5511999999999
const phoneNumber2 = formatPhoneNumber('41764791479');    // +41764791479
Supported Countries: The API automatically validates and routes messages to 200+ countries including Brazil (+55), USA (+1), Switzerland (+41), UK (+44), Germany (+49), France (+33), and many more.

3. API Key Management

❌ Problem: Hardcoded API keys in source code
✅ Solution: Use environment variables or secure storage
// ❌ WRONG - Hardcoded API key
const API_KEY = 'sk_live_a1b2c3d4e5f6...'; // Never do this!

// ✅ CORRECT - Environment variable
const API_KEY = import.meta.env.VITE_WHATSAPP_API_KEY;

// ✅ CORRECT - User input with localStorage
const [apiKey, setApiKey] = useState(
  localStorage.getItem('whatsapp_api_key') || ''
);

4. Orphan Sessions & QR Code Errors

❌ Problem: Error 503 "Session driver not initialized" when requesting QR code
✅ Solution: POST the session again - API will auto-recover and recreate the driver

Cause: This error occurs when:

  • The application was restarted
  • Session exists in database but driver is not loaded in memory
  • These are called "orphan sessions"

How to Fix:

// Step 1: Try creating the session again
const response = await fetch('https://apiwts.top/api/v1/sessions', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'scheduler'
  })
});

// If session exists but driver is missing:
// API will automatically detect and recover the orphan session
// Returns 201 Created (not 409 Conflict)

// Step 2: Get QR code (should work now)
const qrResponse = await fetch('https://apiwts.top/api/v1/sessions/scheduler/qr', {
  headers: { 'X-API-Key': 'your-api-key' }
});
â„šī¸ Auto-Recovery: As of v2.25, the API automatically detects orphan sessions and recreates the driver when you POST the session again. No manual deletion needed!

Alternative (Manual Recovery):

// If auto-recovery doesn't work:
// 1. Delete the session
await fetch('https://apiwts.top/api/v1/sessions/scheduler', {
  method: 'DELETE',
  headers: { 'X-API-Key': 'your-api-key' }
});

// 2. Create it again
await fetch('https://apiwts.top/api/v1/sessions', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-api-key', 'Content-Type': 'application/json' },
  body: JSON.stringify({ sessionId: 'scheduler' })
});

5. Message Sending Errors

❌ Problem: Error 500 "Cannot read properties of undefined" or Error 400 "Session is not ready" when sending messages
✅ Solution: Ensure session is READY before sending messages

Common Causes:

  • Session not authenticated - Session is in QR_PENDING, INITIALIZING, or other non-READY state
  • Session disconnected - WhatsApp session was disconnected and needs re-authentication
  • Invalid phone number - Phone number format is incorrect (must be international format)

Solution - Check Session Status Before Sending:

// Step 1: Check session status
const sessionResponse = await fetch('https://apiwts.top/api/v1/sessions/my-session', {
  headers: { 'X-API-Key': 'your-api-key' }
});

const session = await sessionResponse.json();

// Step 2: Only send if session is READY
if (session.state === 'READY') {
  // Send message
  const response = await fetch('https://apiwts.top/api/v1/messages/text', {
    method: 'POST',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      sessionId: 'my-session',
      to: '+5511999999999',
      text: 'Hello World!'
    })
  });
} else {
  console.error(`Session not ready: ${session.state}`);
  // Handle: show QR code, reconnect, etc.
}
â„šī¸ v2.26 Fix: As of v2.26, the API validates session state before sending messages and returns a clear error message if the session is not READY. Messages are only queued when the session is authenticated.

Error Response Examples:

// Error 400 - Session not ready
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Session my-session is not ready (current state: QR_PENDING). Please ensure the session is authenticated before sending messages."
}

// Error 404 - Session not found
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session my-session not found"
}
✅ v2.27+ Fixed message delivery issues and improved system reliability. The API now correctly handles 200+ country codes without incorrectly adding prefixes to international numbers.

6. Session Management

❌ Problem: QR code expires or session disconnects
✅ Solution: Monitor session status, implement reconnection logic
// Monitor session status
const checkSessionStatus = async (sessionId) => {
  const response = await fetch(`/api/v1/sessions/${sessionId}`, {
    headers: { 'X-API-Key': apiKey }
  });

  const session = await response.json();

  if (session.state === 'DISCONNECTED') {
    console.log('Session disconnected - need to re-authenticate');
    // Get new QR code
  }
};

6.1 Auto-Reconnection (v2.72+)

🔄 New Feature: Automatic reconnection for temporary disconnections
đŸŽ¯ Benefit: Sessions recover automatically from network issues without manual intervention

How Auto-Reconnection Works

  • Temporary Disconnections: Auto-reconnect enabled (3 attempts, 10s interval)
  • Permanent Disconnections: Require manual reconnection via dashboard or API
  • Session State: Only READY/AUTHENTICATED sessions attempt auto-reconnect

Disconnection Types

Reason Type Behavior
CONNECTION_LOST Temporary Auto-reconnect (3 attempts)
TIMEOUT Temporary Auto-reconnect (3 attempts)
CONFLICT Temporary Auto-reconnect (3 attempts)
LOGOUT Permanent Manual reconnect required
NAVIGATION Permanent Manual reconnect required
UNPAIRED Permanent Manual reconnect required

Frontend Auto-Reconnect (Dashboard)

When clicking "Gerar QR Code" after disconnection:

  1. Frontend detects QR code unavailable (404)
  2. Automatically calls reconnect endpoint
  3. Waits 5 seconds for session initialization
  4. Retrieves and displays new QR code
  5. No intermediate prompts - seamless UX

Manual Reconnection via API

Note: Use /restart to disconnect and re-initialize a session. This preserves the session record and triggers a new QR code generation.
// Restart a disconnected session (using /restart endpoint)
const restartSession = async (sessionId) => {
  // Step 1: Restart the session (disconnects + re-initializes)
  const response = await fetch(`/api/v1/sessions/${sessionId}/restart`, {
    method: 'POST',
    headers: { 'X-API-Key': apiKey }
  });

  if (response.ok) {
    console.log('Session restarting...');

    // Step 2: Wait for QR code generation (typically 3-5 seconds)
    setTimeout(async () => {
      const qrResponse = await fetch(`/api/v1/sessions/${sessionId}/qr`, {
        headers: { 'X-API-Key': apiKey }
      });

      if (qrResponse.ok) {
        const blob = await qrResponse.blob();
        const qrUrl = URL.createObjectURL(blob);
        // Display QR code for user to scan
        console.log('QR code ready:', qrUrl);
      }
    }, 5000);
  }
};

// Alternative: Use /connect to re-initialize without full restart
const connectSession = async (sessionId) => {
  const response = await fetch(`/api/v1/sessions/${sessionId}/connect`, {
    method: 'POST',
    headers: { 'X-API-Key': apiKey }
  });
  // Returns 201 if connection initiated, 200 if already connected
};
✅ Best Practice: Monitor session state via webhooks instead of polling. Auto-reconnection happens transparently for temporary issues.

6.2 Zombie Session Detection (v3.8.24+)

🧟 Problem: Sessions can appear READY but silently fail to deliver messages
đŸŽ¯ Solution: Multi-signal detection with automatic recovery

What is a Zombie Session?

A zombie session is a WhatsApp Web session that:

  • Appears healthy: Status shows READY, health check passes
  • API responds success: Send message returns "sent" with valid driverMessageId
  • Messages never arrive: No ACK events, no delivery confirmation
  • Silent failure: No errors, no warnings - just silent message loss

Detection Signals (3 independent checks)

Signal Trigger Condition What it Means
ACK Rate < 20% ACKs in last 3 messages (5 min window) Messages sent but not confirmed by WhatsApp
Heartbeat 2+ consecutive health check failures WWebJS client not responding properly
Error Rate > 50% errors in last 60 seconds High failure rate in message polling

Zombie Confirmation: 2 or more warning flags = ZOMBIE CONFIRMED

Automatic Recovery Flow

  1. 🔍 Detection: Multi-signal check confirms zombie state
  2. 📤 Webhook: session.zombie_detected sent to your endpoint
  3. 🔄 Recovery: Automatic shutdown + reconnection (if AUTO_RECONNECT_ENABLED=true)
  4. ✅ Success: session.recovered webhook OR
  5. ❌ Failure: session.recovery_failed webhook

Webhook Payloads

// session.zombie_detected
{
  "event": "session.zombie_detected",
  "sessionId": "sensyxboutique",
  "data": {
    "metrics": {
      "warningFlags": ["ACK_RATE", "HEARTBEAT"],
      "messagesSent": 5,
      "acksReceived": 0,
      "ackRate": 0,
      "heartbeatFailures": 2,
      "detectedAt": "2026-01-03T05:00:00.000Z"
    },
    "action": "auto_reconnect_initiated",
    "timestamp": 1735880400000
  }
}

// session.recovered
{
  "event": "session.recovered",
  "sessionId": "sensyxboutique",
  "data": {
    "recoveryTime": 1735880430000,
    "timestamp": 1735880430000
  }
}

// session.recovery_failed
{
  "event": "session.recovery_failed",
  "sessionId": "sensyxboutique",
  "data": {
    "error": "Failed to reconnect: timeout",
    "timestamp": 1735880460000
  }
}

Environment Configuration

# Enable/Disable zombie detection
ZOMBIE_DETECTION_ENABLED=true
ZOMBIE_DRY_RUN=false

# ACK Monitoring
ZOMBIE_MIN_MESSAGES_FOR_CHECK=3
ZOMBIE_ACK_RATE_THRESHOLD=0.2
ZOMBIE_ACK_WINDOW_MS=300000

# Heartbeat
ZOMBIE_HEARTBEAT_INTERVAL_MS=30000
ZOMBIE_MAX_HEARTBEAT_FAILURES=2

# Error Rate
ZOMBIE_ERROR_WINDOW_MS=60000
ZOMBIE_ERROR_RATE_THRESHOLD=0.5

# Recovery
AUTO_RECONNECT_ENABLED=true
ZOMBIE_WARNINGS_TO_CONFIRM=2

SLA Guarantees

Metric Target Description
Detection Time < 2 minutes From first failed message to zombie confirmed
Recovery Time < 30 seconds From zombie detected to session reconnected
Webhook Notification < 5 seconds Webhook sent immediately after detection
💡 Tip: Subscribe to session.zombie_detected webhook to monitor zombie events in your logging/alerting system.

💡 Complete Examples

React Component Example

import React, { useState } from 'react';

const WhatsAppIntegration = () => {
  const [apiKey, setApiKey] = useState('');
  const [sessionId, setSessionId] = useState('my-session');
  const [qrCode, setQrCode] = useState(null);
  const [loading, setLoading] = useState(false);

  const testConnection = async () => {
    setLoading(true);
    try {
      const response = await fetch('/health', {
        headers: { 'X-API-Key': apiKey }
      });

      if (response.ok) {
        alert('✅ API Connected!');
      } else {
        alert('❌ Connection failed');
      }
    } catch (error) {
      alert('❌ Network error');
    }
    setLoading(false);
  };

  const getQrCode = async () => {
    setLoading(true);
    try {
      const response = await fetch(`/api/v1/sessions/${sessionId}/qr`, {
        headers: { 'X-API-Key': apiKey }
      });

      if (response.ok) {
        const blob = await response.blob();
        setQrCode(URL.createObjectURL(blob));
      }
    } catch (error) {
      alert('❌ Failed to get QR code');
    }
    setLoading(false);
  };

  const sendMessage = async () => {
    try {
      const response = await fetch('/api/v1/messages/text', {
        method: 'POST',
        headers: {
          'X-API-Key': apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          sessionId,
          to: '+5511999999999',
          text: 'Hello from React!'
        })
      });

      if (response.ok) {
        alert('✅ Message sent!');
      }
    } catch (error) {
      alert('❌ Failed to send message');
    }
  };

  return (
    <div>
      <h1>WhatsApp Integration</h1>

      <input
        type="password"
        placeholder="API Key"
        value={apiKey}
        onChange={(e) => setApiKey(e.target.value)}
      />

      <input
        type="text"
        placeholder="Session ID"
        value={sessionId}
        onChange={(e) => setSessionId(e.target.value)}
      />

      <button onClick={testConnection} disabled={loading}>
        Test Connection
      </button>

      <button onClick={getQrCode} disabled={loading}>
        Get QR Code
      </button>

      <button onClick={sendMessage} disabled={loading}>
        Send Test Message
      </button>

      {qrCode && (
        <div>
          <h3>Scan with WhatsApp</h3>
          <img src={qrCode} alt="QR Code" />
        </div>
      )}
    </div>
  );
};

export default WhatsAppIntegration;

📚 API Information

Access OpenAPI specifications and API metadata programmatically.

GET /api/v1/openapi.json

Get the complete OpenAPI 3.0 specification as JSON (machine-readable API documentation).

// Request (no authentication required)
const response = await fetch('https://apiwts.top/api/v1/openapi.json');
const spec = await response.json();

// Response 200 - OpenAPI Specification
{
  "openapi": "3.0.0",
  "info": {
    "title": "WhatsApp Multi-Driver API",
    "version": "2.52.0",
    "description": "Enterprise-grade WhatsApp API with multi-driver support"
  },
  "servers": [
    {
      "url": "https://apiwts.top/api/v1",
      "description": "Production server"
    }
  ],
  "paths": {
    "/sessions": { ... },
    "/messages/text": { ... }
  },
  "components": {
    "schemas": { ... },
    "securitySchemes": { ... }
  }
}

GET /api/v1/openapi.yaml

Get the complete OpenAPI 3.0 specification as YAML (human-readable format).

// Request (no authentication required)
const response = await fetch('https://apiwts.top/api/v1/openapi.yaml');
const specYaml = await response.text();

// Response 200 - OpenAPI Specification (YAML)
openapi: 3.0.0
info:
  title: WhatsApp Multi-Driver API
  version: 2.52.0
  description: Enterprise-grade WhatsApp API with multi-driver support
servers:
  - url: https://apiwts.top/api/v1
    description: Production server
paths:
  /sessions:
    post:
      summary: Create a new WhatsApp session
      ...

GET /api/v1/info

Get API version, status, and capabilities (lightweight metadata endpoint).

// Request (no authentication required)
const response = await fetch('https://apiwts.top/api/v1/info');

// Response 200
{
  "name": "WhatsApp Multi-Driver API",
  "version": "2.52.0",
  "description": "Enterprise-grade WhatsApp API with multi-driver support",
  "environment": "production",
  "documentation": {
    "swagger": "https://apiwts.top/docs",
    "openapi": "https://apiwts.top/api/v1/openapi.json"
  },
  "capabilities": {
    "drivers": [
      "wwebjs",
      "cloud"
    ],
    "authentication": [
      "apiKey",
      "jwt"
    ],
    "features": [
      "multi-tenant",
      "rate-limiting",
      "message-queue",
      "webhooks",
      "media-upload",
      "qr-code",
      "auto-reconnect"
    ]
  },
  "support": {
    "contact": "support@apiwhatsapp.com",
    "issues": "https://github.com/Moser007/apiwhatsapp/issues"
  }
}