WA-Connect API

Self-hosted WhatsApp Web API for cPanel

Send messages, manage sessions, and integrate WhatsApp into your applications

Overview

Base URL
https://your-domain.com/api/v1
Response Format

All API responses follow this format:

// Success Response
{
    "success": true,
    "data": { ... },
    "message": "Optional success message"
}

// Error Response
{
    "success": false,
    "message": "Error description",
    "error": "Detailed error message"
}

Authentication

All API requests (except health check) require the X-API-Key header:

X-API-Key: your-api-key-here
API keys can be created from the Settings > API Keys page in the dashboard.
Example Request
curl -X POST https://your-domain.com/api/v1/messages/send-text \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -d '{"to": "968XXXXXXXX", "message": "Hello!"}'

Session Management

GET /api/v1/health No Auth Required

Check if the API server is running.

Response
{
    "success": true,
    "message": "WA-Connect API is running",
    "timestamp": "2026-02-27T10:00:00.000Z"
}
GET /api/v1/session/status

Get current WhatsApp session status.

Response
{
    "success": true,
    "data": {
        "status": "connected",
        "user": {
            "id": "968XXXXXXXX@s.whatsapp.net",
            "name": "Your Name"
        },
        "connected_at": "2026-02-27T10:00:00.000Z"
    }
}
POST /api/v1/session/connect

Start WhatsApp connection. Returns QR code if not authenticated.

Response (QR Code needed)
{
    "success": true,
    "data": {
        "status": "qr_ready",
        "qr": "2@ABC123...base64-qr-data..."
    }
}
Response (Already authenticated)
{
    "success": true,
    "data": {
        "status": "connecting",
        "message": "Reconnecting with saved credentials"
    }
}
POST /api/v1/session/disconnect

Disconnect WhatsApp session (keeps credentials for reconnection).

Response
{
    "success": true,
    "message": "Disconnected successfully"
}
POST /api/v1/session/logout

Logout from WhatsApp and clear all session data.

Response
{
    "success": true,
    "message": "Logged out successfully"
}

Messages

POST /api/v1/messages/send-text

Send a text message to a WhatsApp number.

Request Body
{
    "to": "968XXXXXXXX",
    "message": "Hello from WA-Connect!"
}
Response
{
    "success": true,
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "status": "sent",
        "timestamp": "2026-02-27T10:00:00.000Z"
    }
}
POST /api/v1/messages/send-image

Send an image with optional caption.

Request Body
{
    "to": "968XXXXXXXX",
    "imageUrl": "https://example.com/image.jpg",
    "caption": "Check this out!"  // optional
}
Response
{
    "success": true,
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "status": "sent"
    }
}
POST /api/v1/messages/send-document

Send a document/file.

Request Body
{
    "to": "968XXXXXXXX",
    "documentUrl": "https://example.com/document.pdf",
    "mimetype": "application/pdf",
    "fileName": "Invoice.pdf",
    "caption": "Here is your invoice"  // optional
}
Response
{
    "success": true,
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "status": "sent"
    }
}
POST /api/v1/messages/send-video

Send a video with optional caption.

Request Body
{
    "to": "968XXXXXXXX",
    "videoUrl": "https://example.com/video.mp4",
    "caption": "Watch this!"  // optional
}
Response
{
    "success": true,
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "status": "sent"
    }
}
POST /api/v1/messages/send-audio

Send an audio file or voice note.

Request Body
{
    "to": "968XXXXXXXX",
    "audioUrl": "https://example.com/audio.mp3",
    "ptt": false  // set to true for voice note
}
Response
{
    "success": true,
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "status": "sent"
    }
}
POST /api/v1/messages/send-location

Send a location pin.

Request Body
{
    "to": "968XXXXXXXX",
    "latitude": 23.5880,
    "longitude": 58.3829,
    "name": "Muscat, Oman",       // optional
    "address": "Muscat, Oman"     // optional
}
Response
{
    "success": true,
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "status": "sent"
    }
}
POST /api/v1/messages/send-contact

Send a contact card (vCard).

Request Body
{
    "to": "968XXXXXXXX",
    "contactName": "John Doe",
    "contactPhone": "+96899999999"
}
Response
{
    "success": true,
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "status": "sent"
    }
}
POST /api/v1/messages/send-bulk

Send the same message to multiple recipients.

Request Body
{
    "recipients": ["968XXXXXXXX", "968YYYYYYYY", "968ZZZZZZZZ"],
    "message": "Hello everyone!",
    "delay": 1000  // optional, delay between messages in ms (default: 1000)
}
Response
{
    "success": true,
    "data": {
        "total": 3,
        "successful": 3,
        "failed": 0,
        "results": [
            { "to": "968XXXXXXXX", "success": true, "message_id": "3EB0XXX" },
            { "to": "968YYYYYYYY", "success": true, "message_id": "3EB0YYY" },
            { "to": "968ZZZZZZZZ", "success": true, "message_id": "3EB0ZZZ" }
        ]
    }
}

Message Log

GET /api/v1/messages/log

Get paginated message history.

Query Parameters
ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Items per page
directionstringallFilter: inbound, outbound, or all
Response
{
    "success": true,
    "data": {
        "messages": [
            {
                "id": 1,
                "message_id": "3EB0XXXXX",
                "remote_jid": "968XXXXXXXX@s.whatsapp.net",
                "remote_name": "John Doe",
                "direction": "outbound",
                "message_type": "text",
                "content": "Hello!",
                "status": "sent",
                "created_at": "2026-02-27T10:00:00.000Z"
            }
        ],
        "pagination": {
            "page": 1,
            "limit": 20,
            "total": 150,
            "totalPages": 8
        }
    }
}
GET /api/v1/messages/:id

Get a single message by ID or message_id.

Response
{
    "success": true,
    "data": {
        "id": 1,
        "message_id": "3EB0XXXXX",
        "remote_jid": "968XXXXXXXX@s.whatsapp.net",
        "remote_name": "John Doe",
        "direction": "outbound",
        "message_type": "text",
        "content": "Hello!",
        "status": "sent",
        "created_at": "2026-02-27T10:00:00.000Z"
    }
}

Error Handling

HTTP Status Codes
CodeDescription
200Success
400Bad Request - Missing or invalid parameters
401Unauthorized - Invalid or missing API key
403Forbidden - API key lacks required permission
404Not Found - Resource not found
429Too Many Requests - Rate limit exceeded
500Internal Server Error
503Service Unavailable - WhatsApp not connected
Error Response Example
{
    "success": false,
    "message": "WhatsApp is not connected",
    "error": "Service unavailable"
}

Rate Limits

API requests are rate-limited to prevent abuse:

Endpoint TypeLimit
General API100 requests per 15 minutes
Message Sending30 messages per minute
API Key Daily LimitConfigurable per key (default: 1000)
When rate limit is exceeded, you'll receive a 429 Too Many Requests response. Wait before retrying.

Webhooks

Overview

Webhooks allow your application to receive real-time notifications when events occur in WA-Connect. When an event is triggered, WA-Connect sends an HTTP POST request to your configured URL.

Event Types
EventDescription
message.receivedA new message was received
message.sentA message was sent successfully
message.statusMessage delivery status updated (delivered, read)
connection.connectedWhatsApp session connected
connection.disconnectedWhatsApp session disconnected
connection.qr_readyQR code is ready for scanning
call.incomingIncoming call received
call.rejectedIncoming call was rejected
Webhook Payload Format

All webhook requests are sent as HTTP POST with JSON body:

{
    "event": "message.received",
    "timestamp": "2026-02-27T10:00:00.000Z",
    "data": {
        // Event-specific data
    }
}
Request Headers
HeaderDescription
Content-Typeapplication/json
User-AgentWA-Connect-Webhook/1.0
X-Webhook-EventThe event type
X-Webhook-TimestampEvent timestamp (ISO 8601)
X-Webhook-SignatureHMAC-SHA256 signature (if secret configured)
X-Webhook-Signature-256sha256=SIGNATURE (GitHub-style format)
Signature Verification

If you configure a secret for your webhook, WA-Connect signs every request using HMAC-SHA256. You should verify this signature to ensure the request is authentic.

Verification Example (Node.js)
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
    const expected = crypto
        .createHmac('sha256', secret)
        .update(JSON.stringify(payload))
        .digest('hex');

    return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expected)
    );
}

// In your webhook handler:
app.post('/webhook', (req, res) => {
    const signature = req.headers['x-webhook-signature'];
    const isValid = verifySignature(req.body, signature, 'your-secret');

    if (!isValid) {
        return res.status(401).send('Invalid signature');
    }

    // Process the webhook...
    res.status(200).send('OK');
});
EVENT message.received

Triggered when a new message is received.

Payload
{
    "event": "message.received",
    "timestamp": "2026-02-27T10:00:00.000Z",
    "data": {
        "message_id": "3EB0XXXXX",
        "from": "968XXXXXXXX@s.whatsapp.net",
        "from_name": "John Doe",
        "type": "text",
        "content": "Hello!",
        "media_url": null,
        "is_group": false,
        "timestamp": "2026-02-27T10:00:00.000Z"
    }
}
EVENT message.sent

Triggered when a message is sent successfully.

Payload
{
    "event": "message.sent",
    "timestamp": "2026-02-27T10:00:00.000Z",
    "data": {
        "message_id": "3EB0XXXXX",
        "to": "968XXXXXXXX@s.whatsapp.net",
        "type": "text",
        "content": "Hello!",
        "status": "sent"
    }
}
EVENT message.status

Triggered when message delivery status is updated.

Payload
{
    "event": "message.status",
    "timestamp": "2026-02-27T10:00:00.000Z",
    "data": {
        "message_id": "3EB0XXXXX",
        "remote_jid": "968XXXXXXXX@s.whatsapp.net",
        "status": "delivered"  // "sent", "delivered", "read", "failed"
    }
}
EVENT connection.connected

Triggered when WhatsApp session is connected.

Payload
{
    "event": "connection.connected",
    "timestamp": "2026-02-27T10:00:00.000Z",
    "data": {
        "status": "connected",
        "phone_number": "968XXXXXXXX",
        "wa_name": "Your Name"
    }
}
EVENT connection.disconnected

Triggered when WhatsApp session is disconnected.

Payload
{
    "event": "connection.disconnected",
    "timestamp": "2026-02-27T10:00:00.000Z",
    "data": {
        "status": "disconnected",
        "reason": "logout"
    }
}
EVENT call.incoming

Triggered when an incoming call is received.

Payload
{
    "event": "call.incoming",
    "timestamp": "2026-02-27T10:00:00.000Z",
    "data": {
        "from": "968XXXXXXXX@s.whatsapp.net",
        "call_id": "CALL_ID_HERE",
        "is_video": false
    }
}
Retry Logic

If your webhook endpoint returns a non-2xx status code or times out, WA-Connect will retry the delivery:

  • Default retry attempts: 3 (configurable per webhook)
  • Retry delay: 30 seconds between attempts
  • Timeout: 30 seconds per request
Your endpoint should respond with HTTP 200 within 30 seconds to acknowledge receipt.

Contacts

POST /api/v1/contacts/check

Check if a phone number is registered on WhatsApp.

Request Body
{
    "phone": "968XXXXXXXX"
}
Response (Number on WhatsApp)
{
    "success": true,
    "exists": true,
    "message": "Number is on WhatsApp",
    "contact": {
        "jid": "968XXXXXXXX@s.whatsapp.net",
        "phone": "968XXXXXXXX",
        "name": "John Doe"
    }
}
Response (Number NOT on WhatsApp)
{
    "success": true,
    "exists": false,
    "message": "Number is not on WhatsApp"
}
GET /api/v1/contacts

Get list of synced contacts.

Query Parameters
ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger50Items per page
searchstring-Search by name or phone
typestringallFilter: individual, group, or all
Response
{
    "success": true,
    "data": {
        "contacts": [
            {
                "id": 1,
                "jid": "968XXXXXXXX@s.whatsapp.net",
                "phone": "968XXXXXXXX",
                "push_name": "John Doe",
                "is_group": false,
                "last_message_at": "2026-02-27T10:00:00.000Z"
            }
        ],
        "pagination": {
            "page": 1,
            "limit": 50,
            "total": 150,
            "totalPages": 3
        }
    }
}
GET /api/v1/contacts/:jid

Get a single contact by JID.

Response
{
    "success": true,
    "data": {
        "id": 1,
        "jid": "968XXXXXXXX@s.whatsapp.net",
        "phone": "968XXXXXXXX",
        "push_name": "John Doe",
        "is_group": false,
        "message_count": 25,
        "last_message_at": "2026-02-27T10:00:00.000Z",
        "created_at": "2026-02-20T08:00:00.000Z"
    }
}
GET /api/v1/contacts/search

Search contacts by name or phone number.

Query Parameters
ParameterTypeDescription
qstringSearch query (minimum 2 characters)
Response
{
    "success": true,
    "data": {
        "contacts": [
            {
                "id": 1,
                "jid": "968XXXXXXXX@s.whatsapp.net",
                "phone": "968XXXXXXXX",
                "push_name": "John Doe"
            }
        ]
    }
}
GET /api/v1/contacts/export

Export all contacts as CSV file.

Response

Returns a CSV file download with columns: jid, phone, name, is_group, message_count, last_message_at

POST /api/v1/contacts/sync

Trigger a manual contact sync from WhatsApp. Note: Contacts are synced automatically when WhatsApp connects.

Response
{
    "success": true,
    "message": "Contacts are synced automatically when WhatsApp connects."
}
Group Contacts

Group chats are also stored as contacts with is_group: true. You can filter them using the type parameter:

GET /api/v1/contacts?type=group