Self-hosted WhatsApp Web API for cPanel
Send messages, manage sessions, and integrate WhatsApp into your applications
https://your-domain.com/api/v1
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"
}
All API requests (except health check) require the X-API-Key header:
X-API-Key: your-api-key-here
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!"}'
/api/v1/health
No Auth Required
Check if the API server is running.
{
"success": true,
"message": "WA-Connect API is running",
"timestamp": "2026-02-27T10:00:00.000Z"
}
/api/v1/session/status
Get current WhatsApp session status.
{
"success": true,
"data": {
"status": "connected",
"user": {
"id": "968XXXXXXXX@s.whatsapp.net",
"name": "Your Name"
},
"connected_at": "2026-02-27T10:00:00.000Z"
}
}
/api/v1/session/connect
Start WhatsApp connection. Returns QR code if not authenticated.
{
"success": true,
"data": {
"status": "qr_ready",
"qr": "2@ABC123...base64-qr-data..."
}
}
{
"success": true,
"data": {
"status": "connecting",
"message": "Reconnecting with saved credentials"
}
}
/api/v1/session/disconnect
Disconnect WhatsApp session (keeps credentials for reconnection).
{
"success": true,
"message": "Disconnected successfully"
}
/api/v1/session/logout
Logout from WhatsApp and clear all session data.
{
"success": true,
"message": "Logged out successfully"
}
/api/v1/messages/send-text
Send a text message to a WhatsApp number.
{
"to": "968XXXXXXXX",
"message": "Hello from WA-Connect!"
}
{
"success": true,
"data": {
"message_id": "3EB0XXXXX",
"to": "968XXXXXXXX@s.whatsapp.net",
"status": "sent",
"timestamp": "2026-02-27T10:00:00.000Z"
}
}
/api/v1/messages/send-image
Send an image with optional caption.
{
"to": "968XXXXXXXX",
"imageUrl": "https://example.com/image.jpg",
"caption": "Check this out!" // optional
}
{
"success": true,
"data": {
"message_id": "3EB0XXXXX",
"to": "968XXXXXXXX@s.whatsapp.net",
"status": "sent"
}
}
/api/v1/messages/send-document
Send a document/file.
{
"to": "968XXXXXXXX",
"documentUrl": "https://example.com/document.pdf",
"mimetype": "application/pdf",
"fileName": "Invoice.pdf",
"caption": "Here is your invoice" // optional
}
{
"success": true,
"data": {
"message_id": "3EB0XXXXX",
"to": "968XXXXXXXX@s.whatsapp.net",
"status": "sent"
}
}
/api/v1/messages/send-video
Send a video with optional caption.
{
"to": "968XXXXXXXX",
"videoUrl": "https://example.com/video.mp4",
"caption": "Watch this!" // optional
}
{
"success": true,
"data": {
"message_id": "3EB0XXXXX",
"to": "968XXXXXXXX@s.whatsapp.net",
"status": "sent"
}
}
/api/v1/messages/send-audio
Send an audio file or voice note.
{
"to": "968XXXXXXXX",
"audioUrl": "https://example.com/audio.mp3",
"ptt": false // set to true for voice note
}
{
"success": true,
"data": {
"message_id": "3EB0XXXXX",
"to": "968XXXXXXXX@s.whatsapp.net",
"status": "sent"
}
}
/api/v1/messages/send-location
Send a location pin.
{
"to": "968XXXXXXXX",
"latitude": 23.5880,
"longitude": 58.3829,
"name": "Muscat, Oman", // optional
"address": "Muscat, Oman" // optional
}
{
"success": true,
"data": {
"message_id": "3EB0XXXXX",
"to": "968XXXXXXXX@s.whatsapp.net",
"status": "sent"
}
}
/api/v1/messages/send-contact
Send a contact card (vCard).
{
"to": "968XXXXXXXX",
"contactName": "John Doe",
"contactPhone": "+96899999999"
}
{
"success": true,
"data": {
"message_id": "3EB0XXXXX",
"to": "968XXXXXXXX@s.whatsapp.net",
"status": "sent"
}
}
/api/v1/messages/send-bulk
Send the same message to multiple recipients.
{
"recipients": ["968XXXXXXXX", "968YYYYYYYY", "968ZZZZZZZZ"],
"message": "Hello everyone!",
"delay": 1000 // optional, delay between messages in ms (default: 1000)
}
{
"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" }
]
}
}
/api/v1/messages/log
Get paginated message history.
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 20 | Items per page |
direction | string | all | Filter: inbound, outbound, or all |
{
"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
}
}
}
/api/v1/messages/:id
Get a single message by ID or message_id.
{
"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"
}
}
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request - Missing or invalid parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - API key lacks required permission |
404 | Not Found - Resource not found |
429 | Too Many Requests - Rate limit exceeded |
500 | Internal Server Error |
503 | Service Unavailable - WhatsApp not connected |
{
"success": false,
"message": "WhatsApp is not connected",
"error": "Service unavailable"
}
API requests are rate-limited to prevent abuse:
| Endpoint Type | Limit |
|---|---|
| General API | 100 requests per 15 minutes |
| Message Sending | 30 messages per minute |
| API Key Daily Limit | Configurable per key (default: 1000) |
429 Too Many Requests response.
Wait before retrying.
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 | Description |
|---|---|
message.received | A new message was received |
message.sent | A message was sent successfully |
message.status | Message delivery status updated (delivered, read) |
connection.connected | WhatsApp session connected |
connection.disconnected | WhatsApp session disconnected |
connection.qr_ready | QR code is ready for scanning |
call.incoming | Incoming call received |
call.rejected | Incoming call was rejected |
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
}
}
| Header | Description |
|---|---|
Content-Type | application/json |
User-Agent | WA-Connect-Webhook/1.0 |
X-Webhook-Event | The event type |
X-Webhook-Timestamp | Event timestamp (ISO 8601) |
X-Webhook-Signature | HMAC-SHA256 signature (if secret configured) |
X-Webhook-Signature-256 | sha256=SIGNATURE (GitHub-style format) |
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.
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');
});
message.received
Triggered when a new message is received.
{
"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"
}
}
message.sent
Triggered when a message is sent successfully.
{
"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"
}
}
message.status
Triggered when message delivery status is updated.
{
"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"
}
}
connection.connected
Triggered when WhatsApp session is connected.
{
"event": "connection.connected",
"timestamp": "2026-02-27T10:00:00.000Z",
"data": {
"status": "connected",
"phone_number": "968XXXXXXXX",
"wa_name": "Your Name"
}
}
connection.disconnected
Triggered when WhatsApp session is disconnected.
{
"event": "connection.disconnected",
"timestamp": "2026-02-27T10:00:00.000Z",
"data": {
"status": "disconnected",
"reason": "logout"
}
}
call.incoming
Triggered when an incoming call is received.
{
"event": "call.incoming",
"timestamp": "2026-02-27T10:00:00.000Z",
"data": {
"from": "968XXXXXXXX@s.whatsapp.net",
"call_id": "CALL_ID_HERE",
"is_video": false
}
}
If your webhook endpoint returns a non-2xx status code or times out, WA-Connect will retry the delivery:
/api/v1/contacts/check
Check if a phone number is registered on WhatsApp.
{
"phone": "968XXXXXXXX"
}
{
"success": true,
"exists": true,
"message": "Number is on WhatsApp",
"contact": {
"jid": "968XXXXXXXX@s.whatsapp.net",
"phone": "968XXXXXXXX",
"name": "John Doe"
}
}
{
"success": true,
"exists": false,
"message": "Number is not on WhatsApp"
}
/api/v1/contacts
Get list of synced contacts.
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 50 | Items per page |
search | string | - | Search by name or phone |
type | string | all | Filter: individual, group, or all |
{
"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
}
}
}
/api/v1/contacts/:jid
Get a single contact by JID.
{
"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"
}
}
/api/v1/contacts/search
Search contacts by name or phone number.
| Parameter | Type | Description |
|---|---|---|
q | string | Search query (minimum 2 characters) |
{
"success": true,
"data": {
"contacts": [
{
"id": 1,
"jid": "968XXXXXXXX@s.whatsapp.net",
"phone": "968XXXXXXXX",
"push_name": "John Doe"
}
]
}
}
/api/v1/contacts/export
Export all contacts as CSV file.
Returns a CSV file download with columns: jid, phone, name, is_group, message_count, last_message_at
/api/v1/contacts/sync
Trigger a manual contact sync from WhatsApp. Note: Contacts are synced automatically when WhatsApp connects.
{
"success": true,
"message": "Contacts are synced automatically when WhatsApp connects."
}
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