Overview
This guide explains how to integrate your application with our WhatsApp messaging platform using API keys. With the Integration API you can send messages, manage contacts and templates, and monitor instances programmatically.
Fast & Reliable
Queue-based processing with automatic retries ensures message delivery
Secure
API key authentication with granular per-endpoint permissions
Scalable
Handle thousands of messages per hour with our robust infrastructure
Getting Started
Prerequisites
- An active account on the platform
- At least one connected WhatsApp instance
- An Integration API key with the required permissions
- Sufficient credits in your WhatsApp pocket (1 credit per message)
Base URL
https://app.wessaal.com/integration/v1
Authentication
Using API Keys
All Integration API requests require an API key sent as a request header:
X-API-Key: sk_live_your_api_key_here
Authentication Flow
Generate API Key
Create an API key from your dashboard settings. Choose only the permissions your integration needs.
Store API Key Securely
Save the key in an environment variable. It is shown only once at creation time.
Include in Every Request
Add the X-API-Key header to every call to the Integration API.
Permissions
Each API key is granted a set of permission scopes. A request will be rejected with 403 Forbidden if the key lacks the required scope.
Credits
Every outgoing message deducts 1 credit from your WhatsApp pocket at the moment it is queued. If the send ultimately fails, the credit is automatically refunded.
402 Payment Required and the message is not queued.Ping
Quick connectivity check. Returns pong with a server timestamp.
Required Permission
Response
{
"success": true,
"message": "pong",
"timestamp": "2025-01-15T10:30:00.000000Z"
}
API Key Info (me)
Returns the authenticated account details, the API key's granted permissions, and current credit balances for all pockets.
Required Permission
Response
{
"success": true,
"data": {
"user": {
"id": 93,
"name": "Ahmed",
"email": "ahmed@example.com"
},
"api_key": {
"name": "My Integration Key",
"permissions": ["send_message", "read_messages"],
"created_at": "2025-01-01T00:00:00.000000Z"
},
"pockets": {
"whatsapp": { "balance": 250, "currency": "credits" },
"sms": { "balance": 0, "currency": "credits" }
}
}
}
Send Message
Send a WhatsApp message to a single recipient. Supports text, media (image, video, document, audio), and templates.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Required | Instance ID (integer) or instance name (string) |
recipient | string | Optional* | Phone in E.164 format, e.g. +966501234567. Required if contact_id is absent. |
contact_id | integer | Optional* | Contact record ID — alternative to recipient. |
message_type | string | Optional | One of: text (default), image, video, document, audio |
content | string | Optional* | Message text or media caption. Required unless template_id is provided. |
media_url | string (URL) | Conditional | Public HTTPS URL of the media file. Required when message_type is image, video, document, or audio. |
filename | string | Conditional | Filename displayed in chat (e.g. invoice.pdf). Required when message_type is image, video, document, or audio. |
template_id | integer | Optional | ID of a saved message template. When set, content is taken from the template. |
template_variables | object | Optional | Key-value map to fill {{variable}} placeholders in the template. |
delay | integer | Optional | Artificial delay in milliseconds before the message is dispatched. |
quoted | object | Optional | Quote/reply to an existing message. Pass {"key":{"id":"WHATSAPP_MSG_ID"}}. |
link_preview | boolean | Optional | Enable URL preview in text messages (default: true). |
mentioned | array | Optional | Array of JIDs to @-mention, e.g. ["966501234567@s.whatsapp.net"]. |
mentions_everyone | boolean | Optional | Mention all group members (group chats only). |
recipient or contact_id must be provided. * Content: Either content or template_id must be provided.Request Examples
Text message
{
"instance": "my-instance",
"recipient": "+966501234567",
"message_type": "text",
"content": "Hello from our API!"
}
Image message
{
"instance": "my-instance",
"recipient": "+966501234567",
"message_type": "image",
"content": "Here is your invoice",
"media_url": "https://example.com/invoice.png",
"filename": "invoice.png"
}
Using a template
{
"instance": "my-instance",
"recipient": "+966501234567",
"template_id": 7,
"template_variables": {
"name": "Ahmed",
"order_id": "ORD-9988"
}
}
Response (202 Accepted)
{
"success": true,
"message": "Message queued for sending",
"data": {
"message_id": 12345,
"status": "pending",
"type": "text",
"recipient": "+966501234567",
"queued_at": "2025-01-15T10:30:00.000000Z",
"instance": { "id": 124, "name": "my-instance" },
"template_used": false,
"credits_charged": 1
}
}
Send Bulk Messages
Send up to 100 messages in a single API call. Each message is queued independently — partial success is returned when only some messages succeed.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Required | Instance ID or name used for all messages |
template_id | integer | Optional | Shared template applied to messages that have no individual content |
template_variables | object | Optional | Default variable values. Per-message overrides take precedence. |
messages | array | Required | Array of 1–100 message objects (same fields as Send Message) |
Request Example
{
"instance": "my-instance",
"messages": [
{
"recipient": "+966501234567",
"message_type": "text",
"content": "Hello Customer 1!"
},
{
"recipient": "+966509876543",
"message_type": "text",
"content": "Hello Customer 2!"
},
{
"recipient": "+971501234567",
"message_type": "image",
"content": "Your receipt",
"media_url": "https://example.com/receipt.png",
"filename": "receipt.png"
}
]
}
Response (200 OK)
{
"success": true,
"message": "Bulk: 3 queued, 0 failed",
"data": {
"total": 3,
"success_count": 3,
"failure_count": 0,
"results": [
{ "index": 0, "recipient": "+966501234567", "success": true, "message_id": 12345, "status": "pending" },
{ "index": 1, "recipient": "+966509876543", "success": true, "message_id": 12346, "status": "pending" },
{ "index": 2, "recipient": "+971501234567", "success": true, "message_id": 12347, "status": "pending" }
]
}
}
success_count and failure_count in the response.Send Status (Story)
Broadcast a WhatsApp Status (Story) to your contacts.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Required | Instance ID or name |
type | string | Required | Story type: text, image, video, or audio |
content | string | Required | Text content (text type) or public media URL (image/video/audio) |
caption | string | Optional | Caption shown under media stories |
background_color | string | Optional | Hex background color for text stories, e.g. #25D366 |
font | integer | Optional | Font style 1–7 (text stories only) |
all_contacts | boolean | Optional | Broadcast to all contacts (default: true) |
status_jid_list | array | Optional | Specific contact JIDs to target (overrides all_contacts) |
Request Example
{
"instance": "my-instance",
"type": "text",
"content": "New products just dropped!",
"background_color": "#25D366",
"font": 2,
"all_contacts": true
}
Send Location
Send a location pin that opens in WhatsApp Maps.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Required | Instance ID or name |
recipient | string | Optional* | Phone in E.164 format |
contact_id | integer | Optional* | Contact record ID (alternative to recipient) |
latitude | number | Required | Decimal latitude, -90 to 90 |
longitude | number | Required | Decimal longitude, -180 to 180 |
name | string | Optional | Display name for the pin |
address | string | Optional | Street address shown below the pin |
Request Example
{
"instance": "my-instance",
"recipient": "+966501234567",
"latitude": 24.7136,
"longitude": 46.6753,
"name": "Our Head Office",
"address": "King Fahd Road, Riyadh"
}
Send Contact Card
Send one or more vCard contacts that the recipient can save to their phone.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Required | Instance ID or name |
recipient | string | Optional* | Phone in E.164 format |
contact_id | integer | Optional* | Contact record ID (alternative to recipient) |
contacts | array | Required | Array of vCard contact objects (see fields below) |
Contact Object Fields
| Field | Type | Required | Description |
|---|---|---|---|
fullName | string | Required | Contact display name |
wuid | string | Required | Digits only, no + (e.g. 966501234567) |
phoneNumber | string | Required | Display phone number (can include +) |
organization | string | Optional | Company name |
email | string | Optional | Email address |
url | string | Optional | Website URL |
Request Example
{
"instance": "my-instance",
"recipient": "+966501234567",
"contacts": [
{
"fullName": "Support Team",
"wuid": "966200001234",
"phoneNumber": "+966200001234",
"organization": "Acme Corp",
"email": "support@acme.com"
}
]
}
Send Poll
Send an interactive poll. Recipients vote directly inside WhatsApp.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Required | Instance ID or name |
recipient | string | Optional* | Phone in E.164 format |
contact_id | integer | Optional* | Contact record ID (alternative to recipient) |
name | string | Required | Poll question (max 255 characters) |
values | array | Required | Array of option strings (minimum 2, maximum 13) |
selectable_count | integer | Optional | Max options a user can select (default: 1) |
delay | integer | Optional | Artificial delay in milliseconds before dispatch |
quoted | object | Optional | Quote/reply to an existing message |
Request Example
{
"instance": "my-instance",
"recipient": "+966501234567",
"name": "What is your preferred contact time?",
"values": ["Morning", "Afternoon", "Evening"],
"selectable_count": 1
}
Get Message Status
Check the current delivery status of a specific sent message.
Required Permission
Response
{
"success": true,
"data": {
"message_id": 12345,
"status": "delivered",
"type": "text",
"recipient": "+966501234567",
"sent_at": "2025-01-15T10:30:05.000000Z",
"delivered_at": "2025-01-15T10:30:07.000000Z",
"failed_at": null,
"error_message": null
}
}
Status Values
Get Message Detail
Retrieve full details of a single message record, including content, metadata, and all status timestamps.
Required Permission
List Messages
Get a paginated list of all messages for the authenticated account.
Required Permission
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
per_page | integer | 50 | Results per page (max 100) |
status | string | — | Filter: pending, sent, delivered, failed |
type | string | — | Filter by message type, e.g. text, image |
Response
{
"success": true,
"data": {
"current_page": 1,
"data": [
{
"id": 12345,
"type": "text",
"recipient": "+966501234567",
"content": "Hello!",
"status": "delivered",
"created_at": "2025-01-15T10:30:00.000000Z"
}
],
"per_page": 50,
"total": 150
}
}
Instance Management
List Instances
Get all connected WhatsApp instances for your account.
Required Permission
Response
{
"success": true,
"data": [
{
"id": 124,
"name": "my-instance",
"status": "open",
"workspace": { "id": 22, "name": "Main Workspace" },
"created_at": "2025-01-01T00:00:00.000000Z"
}
]
}
Get Instance Details
Get full details of a specific instance by ID or name.
Required Permission
Get Instance Live Status
Query the live connection status from the underlying WhatsApp gateway in real time.
Required Permission
Response
{
"success": true,
"data": {
"instance_id": 124,
"name": "my-instance",
"connection_status": "open",
"phone_number": "+966501234567",
"battery": 85,
"plugged": true
}
}
Connection Status Values
Contact Management
List Contacts
Get all contact records for your account.
Required Permission
Create Contact
Add a new contact. The returned id can be used as contact_id in any send endpoint.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Required | Phone in E.164 format, e.g. +966501234567 |
label | string | Optional | Display name / label for the contact |
email | string | Optional | Email address |
notes | string | Optional | Free-text notes about the contact |
Request Body
{
"phone": "+966501234567",
"label": "Ahmed Al-Rashid",
"email": "ahmed@example.com",
"notes": "VIP customer"
}
Get Contact
Get a single contact record by ID.
Required Permission
Update Contact
Update an existing contact. Only include fields you want to change.
Required Permission
Request Body
{
"label": "Ahmed Al-Rashid (Updated)",
"notes": "Upgraded to Premium"
}
Delete Contact
Permanently delete a contact record.
Required Permission
Template Management
Create Template
Create a reusable message template with {{variable}} placeholders. Use the returned id as template_id in send requests.
Required Permission
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
workspace_id | integer | Required | Workspace the template belongs to |
name | string | Required | Internal name for the template |
message_type | string | Required | One of: text, image, video, document, audio |
content | string | Required | Template body. Use {{variable_name}} for placeholders. |
is_active | boolean | Optional | Enable/disable the template (default: true) |
Request Example
{
"workspace_id": 22,
"name": "Order Shipped",
"message_type": "text",
"content": "Hi {{name}}, your order #{{order_id}} has shipped! Track it at {{tracking_url}}.",
"is_active": true
}
Response (201 Created)
{
"success": true,
"data": {
"id": 7,
"name": "Order Shipped",
"message_type": "text",
"is_active": true,
"workspace_id": 22,
"created_at": "2025-01-15T09:00:00.000000Z"
}
}
List Templates
Get all message templates for the authenticated account.
Required Permission
Workspace Management
List Workspaces
Get all workspaces belonging to the authenticated account.
Required Permission
Get Workspace
Get full details of a single workspace.
Required Permission
Disabled Endpoints
The following endpoints are permanently disabled and return 410 Gone regardless of the request body.
Error Handling
HTTP Status Codes
errors fieldError Response Shape
{
"success": false,
"message": "Validation failed",
"errors": {
"instance": ["The instance field is required."],
"media_url": ["The media url field is required when message type is image."]
}
}
{
"success": false,
"message": "Insufficient credits: not enough balance",
"error_code": "INSUFFICIENT_CREDITS"
}
Code Examples
Ready-to-use snippets in multiple languages.
use Illuminate\Support\Facades\Http;
$apiKey = 'sk_live_your_api_key_here';
$response = Http::withHeaders([
'X-API-Key' => $apiKey,
])->post('https://app.wessaal.com/integration/v1/messages/send', [
'instance' => 'my-instance',
'recipient' => '+966501234567',
'message_type' => 'text',
'content' => 'Hello from PHP!',
]);
if ($response->successful()) {
$data = $response->json();
echo 'Queued! ID: ' . $data['data']['message_id'];
echo ' | Credits: ' . $data['data']['credits_charged'];
} else {
$error = $response->json();
echo 'Error ' . $response->status() . ': ' . $error['message'];
}
const axios = require('axios');
const apiKey = 'sk_live_your_api_key_here';
async function sendMessage() {
try {
const response = await axios.post(
'https://app.wessaal.com/integration/v1/messages/send',
{
instance: 'my-instance',
recipient: '+966501234567',
message_type: 'text',
content: 'Hello from Node.js!'
},
{ headers: { 'X-API-Key': apiKey } }
);
const { message_id, credits_charged } = response.data.data;
console.log(`Queued! ID: ${message_id} | Credits: ${credits_charged}`);
} catch (error) {
const status = error.response?.status;
const msg = error.response?.data?.message || error.message;
console.error(`Error ${status}: ${msg}`);
}
}
sendMessage();
import requests
api_key = 'sk_live_your_api_key_here'
headers = {'X-API-Key': api_key, 'Content-Type': 'application/json'}
payload = {
'instance': 'my-instance',
'recipient': '+966501234567',
'message_type': 'text',
'content': 'Hello from Python!'
}
response = requests.post(
'https://app.wessaal.com/integration/v1/messages/send',
json=payload,
headers=headers
)
if response.status_code == 202:
data = response.json()
msg_id = data['data']['message_id']
credits = data['data']['credits_charged']
print(f'Queued! ID: {msg_id} | Credits charged: {credits}')
else:
error = response.json()
print(f'Error {response.status_code}: {error["message"]}')
curl -X POST https://app.wessaal.com/integration/v1/messages/send \
-H "X-API-Key: sk_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"instance": "my-instance",
"recipient": "+966501234567",
"message_type": "text",
"content": "Hello from cURL!"
}'
Best Practices
Security
- Never expose API keys in client-side code
- Store keys in environment variables only
- Grant only the permissions your integration needs
- Rotate keys regularly
Performance
- Use bulk sending for large campaigns (up to 100 per call)
- Use
contact_idinstead of repeating phone numbers - Use templates for repeating message structures
- Cache instance and workspace IDs locally
Error Handling
- Always check the HTTP status code
- Handle
402by topping up your pocket credits - Handle
410by removing calls to disabled endpoints - Poll
/messages/{id}/statusfor delivery confirmation
Support
Need help? We're here for you 24/7.
Documentation
Check this guide for answers to common integration questions
Email Support
Contact our team for technical assistance and account queries
Community
Join our developer community forum to share and learn