Getting Started
Buka Dashboard

What is WA Gateway?

WA Gateway is a REST API bridge that enables your application to send and receive WhatsApp messages programmatically. It uses the WhatsApp Web protocol to establish a connection between your system and WhatsApp.

Common use cases:

  • Send order confirmations and shipping notifications from your e-commerce platform
  • Deliver OTP codes, booking confirmations, and appointment reminders
  • Build customer service systems that receive and respond to WhatsApp messages

Setup Guide

1

Create an account — Register via the Overview page or the /auth/register API endpoint with your email, name, and password.

2

Create a WhatsApp session — Navigate to the Sessions page, click "New Session", provide a name (e.g. bot-saya), and scan the QR code using your WhatsApp mobile app (Settings > Linked Devices > Link a Device).

3

Generate an API Key — Go to the API Keys page, click "Create API Key", and assign a label. Save the generated key securely — it will only be displayed once.

4

Send your first message — Use the following cURL command to send a message. Replace the placeholders with your actual server URL, API Key, and target phone number.

curl -X POST https://your-server.com/send \
  -H "x-api-key: wa_key_abc123def456" \
  -H "Content-Type: application/json" \
  -d '{"phone":"628123456789","message":"Hello from WA Gateway","session":"bot-saya"}'
5

Done — The recipient will receive the WhatsApp message from your connected number.

Important: Phone numbers must include the country code without the + prefix. Use 628123456789 (62 = Indonesia), not 08123456789 or +628123456789.

Authentication Methods

All API endpoints (except /auth/*) require authentication. WA Gateway supports three authentication methods:

1. JWT Token

Obtained after a successful login. Valid for 7 days. Include it in the request header:

x-access-token: <jwt_token>

2. API Key (recommended for server-to-server)

Persistent credentials that do not expire. Generate them from the API Keys page or via POST /account/api-keys.

x-api-key: <api_key>

Authentication Endpoints

POST /auth/register — Create a new account

{"email": "user@example.com", "password": "password123", "name": "Your Name"}

POST /auth/login — Authenticate and receive a JWT token

{"email": "user@example.com", "password": "password123"}

POST /auth/google — Authenticate using Google OAuth credential

GET /auth/config — Retrieve Google OAuth client configuration

Account & API Key Management

GET /account — Retrieve your profile information

PUT /account — Update your display name

{"name": "New Name"}

GET /account/api-keys — List all API keys associated with your account

POST /account/api-keys — Generate a new API key

{"label": "Production Server"}

DELETE /account/api-keys/:id — Revoke an API key

Session Management

A session represents a connection between WA Gateway and a WhatsApp account. Each session corresponds to one phone number. To connect multiple numbers, create multiple sessions with distinct names.

Note: Each session can only serve one WhatsApp number. For three numbers, create three separate sessions (e.g. bot-1, bot-2, bot-3).

List Sessions

GET /sessions — List all active engine sessions (no authentication required)

GET /my-sessions — List sessions belonging to your account (authentication required)

Create a Session

Two connection methods are available:

  • QR Code — Omit the phoneNumber field to receive a QR code for scanning
  • Pairing Code — Provide a phoneNumber to receive a pairing code that can be entered manually

POST /sessions

{"name": "bot-saya"}
{"name": "bot-saya", "phoneNumber": "628123456789"}
ParameterRequiredDescription
nameYesLowercase letters, numbers, and hyphens only. Example: bot-toko
phoneNumberNo8-15 digits. When provided, generates a pairing code instead of QR.

Additional Operations

PUT /sessions/:name/webhook — Configure webhook URL for incoming messages

DELETE /sessions/:name — Remove a session and disconnect WhatsApp

GET /qr/:name — Retrieve the last generated QR code

GET /pairing-code/:name — Retrieve the last generated pairing code

Send Text & Image Messages

A single endpoint handles both text and image messages. The session parameter is optional — if omitted, the first active session will be used.

POST /send

Text Message

{"phone": "628123456789", "message": "Your order has been shipped!", "session": "bot-saya"}

Image Message

{"phone": "628123456789", "image": "https://example.com/photo.jpg", "message": "Product photo", "session": "bot-saya"}
ParameterRequiredDescription
phoneYesRecipient number with country code, no + prefix. Use target as an alias.
messageNo*Text content. Required if image is not provided.
imageNo*Publicly accessible image URL. Required if message is not provided.
sessionNoSession name. Defaults to the first active session.

Send Documents

Send files such as PDFs, Word documents, or Excel spreadsheets. The file content must be encoded in base64 format.

POST /send-document

{
  "phone": "628123456789",
  "base64": "JVBERi0xLjc...",
  "filename": "report.pdf",
  "mimetype": "application/pdf",
  "caption": "Monthly report",
  "session": "bot-saya"
}
ParameterRequiredDescription
base64YesBase64-encoded file content
filenameYesDisplay name in the WhatsApp chat
mimetypeNoDefaults to application/pdf
captionNoOptional file description

Contact Management

Store customer information and important contacts directly within WA Gateway.

GET /contacts — List all saved contacts

POST /contacts — Create a new contact

{"name": "John Doe", "phone": "628123456789", "email": "john@example.com", "notes": "Regular customer"}

PUT /contacts/:id — Update an existing contact

DELETE /contacts/:id — Remove a contact

POST /contacts/find-by-phone — Look up a contact by phone number

{"phone": "628123456789"}

Phone Number Resolution

WhatsApp now uses a LID (Lightweight ID) system instead of phone numbers. This endpoint resolves a LID JID back to the original phone number by querying multiple data sources.

POST /resolve-phone

{"jid": "1234567890@lid", "senderName": "John Doe"}

Incoming messages? See the Inbox documentation for webhook configuration, real-time events, and message logs.

Overview

There are three ways to receive incoming WhatsApp messages:

  1. Webhook — WA Gateway forwards incoming messages to your server via HTTP POST
  2. Socket.IO — Real-time event stream directly to your client application
  3. Message Logs API — Fetch historical messages with GET /messages

1. Webhook

Configure a webhook URL per session. Every time a message is received, WA Gateway sends an HTTP POST request to your URL with the message data.

PUT /sessions/:name/webhook

{"webhookUrl": "https://your-app.com/webhook", "webhookSecret": "your-secret-key"}

Webhook Payload

{
  "event": "message.incoming",
  "session": "bot-saya",
  "phone": "628123456789",
  "remoteJid": "628123456789@s.whatsapp.net",
  "pushName": "John",
  "message": "Hello, is anyone there?",
  "isGroup": false,
  "timestamp": 1700000000,
  "raw": { ... }
}

If a webhookSecret was configured, the request includes the x-webhook-secret header. Verify this on your server to confirm the request came from WA Gateway.

Note: The webhook URL must be publicly accessible. For development, use webhook.site or ngrok.

2. Socket.IO Real-time Events

Your client application can listen for incoming messages in real time via Socket.IO without polling.

const socket = io("https://your-server.com");

socket.on("message:incoming:bot-saya", (data) => {
    console.log("New message from:", data.phone, data.message);
    // data: { event, session, phone, remoteJid, pushName, message, isGroup, timestamp, raw }
});

Replace bot-saya with your session name. Events include: init, qr:{name}, pairingcode:{name}, ready:{name}, disconnected:{name}, and message:incoming:{name}.

3. Message Logs API

Retrieve all sent and received messages for a session. Incoming messages have "direction": "in".

GET /messages?session=bot-saya&limit=50

{
  "success": true,
  "data": [{
    "id": "clx...",
    "sessionName": "bot-saya",
    "direction": "in",
    "phone": "628123456789",
    "message": "Hello",
    "type": "text",
    "status": "received",
    "remoteJid": "628123456789@s.whatsapp.net",
    "createdAt": "2024-01-01T00:00:00.000Z"
  }]
}

See Logs & Usage for full details on message log queries and deletion.

Webhook Integration Examples

Below are examples of webhook receivers in various languages. Deploy these on your server to handle incoming messages from WA Gateway.

Node.js
PHP
Python
Golang
const express = require("express");
const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
    const secret = req.headers["x-webhook-secret"];
    const { session, phone, message, pushName } = req.body;

    console.log(`[${session}] Message from ${pushName} (${phone}): ${message}`);

    // Your business logic here

    res.sendStatus(200);
});

app.listen(3000);
<?php
$secret = $_SERVER["HTTP_X_WEBHOOK_SECRET"] ?? "";
$input = json_decode(file_get_contents("php://input"), true);

$session  = $input["session"] ?? "";
$phone    = $input["phone"] ?? "";
$message  = $input["message"] ?? "";
$pushName = $input["pushName"] ?? "";

error_log("[{$session}] Message from {$pushName} ({$phone}): {$message}");

http_response_code(200);
from flask import Flask, request

app = Flask(__name__)

@app.route("/webhook", methods=["POST"])
def webhook():
    data = request.json
    secret = request.headers.get("x-webhook-secret")

    session  = data.get("session")
    phone    = data.get("phone")
    message  = data.get("message")
    pushName = data.get("pushName")

    print(f"[{session}] Message from {pushName} ({phone}): {message}")

    return "", 200
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type WebhookPayload struct {
    Session  string `json:"session"`
    Phone    string `json:"phone"`
    Message  string `json:"message"`
    PushName string `json:"pushName"`
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    secret := r.Header.Get("x-webhook-secret")
    var payload WebhookPayload
    json.NewDecoder(r.Body).Decode(&payload)

    log.Printf("[%s] Message from %s (%s): %s",
        payload.Session, payload.PushName, payload.Phone, payload.Message)

    w.WriteHeader(http.StatusOK)
}

func main() {
    http.HandleFunc("/webhook", webhookHandler)
    http.ListenAndServe(":3000", nil)
}

Integration Overview

WA Gateway exposes a simple REST API. Your application — whether it is a PHP website, a Python backend, a Node.js service, or a mobile app — can send HTTP requests to interact with WhatsApp. Below are code examples in popular programming languages.

All API calls require authentication via x-api-key header. Replace https://your-server.com with your actual WA Gateway server URL and wa_key_xxx with your API Key.

cURL

Useful for testing directly from the terminal.

curl -X POST https://your-server.com/send \
  -H "x-api-key: wa_key_abc123" \
  -H "Content-Type: application/json" \
  -d '{"phone":"628123456789","message":"Hello from WA Gateway","session":"bot-saya"}'

PHP (using cURL)

<?php
$apiKey = 'wa_key_abc123';
$serverUrl = 'https://your-server.com';

$data = [
    'phone' => '628123456789',
    'message' => 'Hello from PHP!',
    'session' => 'bot-saya'
];

$ch = curl_init("$serverUrl/send");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    "x-api-key: $apiKey"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
if ($result['success']) {
    echo "Message sent successfully!";
} else {
    echo "Error: " . $result['message'];
}
?>

Python (using requests)

import requests
import json

url = "https://your-server.com/send"
headers = {
    "x-api-key": "wa_key_abc123",
    "Content-Type": "application/json"
}
payload = {
    "phone": "628123456789",
    "message": "Hello from Python!",
    "session": "bot-saya"
}

response = requests.post(url, headers=headers, json=payload)
result = response.json()

if result.get("success"):
    print("Message sent successfully!")
else:
    print(f"Error: {result.get('message')}")

Node.js (using axios)

const axios = require('axios');

const API_KEY = 'wa_key_abc123';
const SERVER_URL = 'https://your-server.com';

async function sendMessage() {
    try {
        const response = await axios.post(`${SERVER_URL}/send`, {
            phone: '628123456789',
            message: 'Hello from Node.js!',
            session: 'bot-saya'
        }, {
            headers: {
                'x-api-key': API_KEY,
                'Content-Type': 'application/json'
            }
        });

        if (response.data.success) {
            console.log('Message sent successfully!');
        }
    } catch (error) {
        console.error('Error:', error.response?.data?.message || error.message);
    }
}

sendMessage();

Golang (using net/http)

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    apiKey := "wa_key_abc123"
    serverUrl := "https://your-server.com"

    payload := map[string]string{
        "phone":   "628123456789",
        "message": "Hello from Golang!",
        "session": "bot-saya",
    }

    jsonData, _ := json.Marshal(payload)
    req, _ := http.NewRequest("POST", serverUrl+"/send", bytes.NewBuffer(jsonData))
    req.Header.Set("x-api-key", apiKey)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)

    if result["success"] == true {
        fmt.Println("Message sent successfully!")
    } else {
        fmt.Println("Error:", result["message"])
    }
}

Receiving Messages via Webhook (PHP Example)

This example shows how to handle incoming WhatsApp messages forwarded by WA Gateway to your webhook URL.

<?php
// Receive webhook from WA Gateway
$input = json_decode(file_get_contents('php://input'), true);

if ($input) {
    $session = $input['session'];
    $phone   = $input['phone'];
    $message = $input['message'];
    $type    = $input['type'];
    
    // Log or process the incoming message
    file_put_contents('messages.log', 
        "[$session] From: $phone - $message\n", 
        FILE_APPEND
    );
    
    // Send an auto-reply
    $reply = [
        'phone' => $phone,
        'message' => "Thank you for your message. We will respond shortly.",
        'session' => $session
    ];
    
    $ch = curl_init("https://your-server.com/send");
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($reply));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'x-api-key: wa_key_abc123'
    ]);
    curl_exec($ch);
    curl_close($ch);
}

http_response_code(200);
?>

Message Logs

Retrieve the history of sent and received messages, with optional filtering by session.

GET /messages?session=bot-saya&limit=50

ParameterRequiredDescription
sessionNoFilter logs by session name
limitNoMaximum records to return (default 50, max 200)
{
  "success": true,
  "data": [{
    "id": "clx...",
    "sessionName": "bot-saya",
    "direction": "out",
    "phone": "628123456789",
    "message": "Hello",
    "type": "text",
    "status": "sent",
    "createdAt": "2024-01-01T00:00:00.000Z"
  }]
}

Delete Conversation History

DELETE /messages — Remove message logs for a specific phone number

{"phone": "628123456789", "session": "bot-saya"}

Usage & Quota

Each account has a monthly message limit based on the assigned tier (Free, Pro, Enterprise).

GET /usage

{
  "success": true,
  "data": {
    "messagesSent": 42,
    "messagesReceived": 18,
    "totalMessages": 60,
    "limitPerMonth": 200,
    "limitSessions": 3,
    "tier": "free",
    "periodStart": "2024-01-01T00:00:00.000Z",
    "periodEnd": "2024-01-31T23:59:59.999Z"
  }
}

A limitPerMonth value of 0 indicates unlimited usage (Enterprise tier).

Rate Limits (per minute)

TierRequests per minuteAffected endpoints
Free10/send, /send-document, /sessions, /my-sessions
Pro60same
Enterprise300same

Exceeding the rate limit returns HTTP 429. Wait before making additional requests.

Session Status

Check whether a specific WhatsApp session is currently connected and ready to send/receive messages.

GET /status?session=bot-saya

{"name": "bot-saya", "connected": true, "phone": "6281234567890"}

Omitting the session parameter returns all sessions with their statuses.

HTTP Error Codes

CodeCause & Solution
400Invalid or missing parameters. Verify the request body contains all required fields with correct formats.
401Authentication failed. Check that your x-access-token or x-api-key header is present and valid.
403Access denied. You do not have permission for this resource (e.g. attempting admin endpoints without admin privileges).
404Resource not found. Verify the session name, contact ID, or other identifiers.
429Rate limit exceeded or monthly quota exhausted. Wait before retrying or upgrade your tier.
500Internal server error. Retry the request. Contact the server administrator if the issue persists.

Error responses follow this format:

{"success": false, "message": "Description of what went wrong"}