API Version 1.0

WhatsApp Integration API

Send messages, manage contacts, and monitor instances programmatically with our powerful REST API

99.9%
Uptime
5000+
Requests/Hour
24/7
Support

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

Integration API v1
https://app.wessaal.com/integration/v1

Authentication

Using API Keys

All Integration API requests require an API key sent as a request header:

HTTP Header
X-API-Key: sk_live_your_api_key_here

Authentication Flow

1

Generate API Key

Create an API key from your dashboard settings. Choose only the permissions your integration needs.

2

Store API Key Securely

Save the key in an environment variable. It is shown only once at creation time.

3

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.

send_message

Queue and send messages, check per-message delivery status

read_messages

Retrieve message records and full message details

manage_instances

List and inspect WhatsApp phone instances

manage_contacts

Create, read, update, and delete contact records

manage_templates

Create and list reusable message templates

manage_workspaces

List and inspect workspaces

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.

Insufficient Credits: If your pocket balance is 0, the API returns 402 Payment Required and the message is not queued.

Ping

Quick connectivity check. Returns pong with a server timestamp.

GET
/integration/v1/ping

Required Permission

Any valid API key

Response

JSON
{
  "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.

GET
/integration/v1/me

Required Permission

Any valid API key

Response

JSON
{
  "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.

POST
/integration/v1/messages/send

Required Permission

send_message

Parameters

ParameterTypeRequiredDescription
instancestringRequiredInstance ID (integer) or instance name (string)
recipientstringOptional*Phone in E.164 format, e.g. +966501234567. Required if contact_id is absent.
contact_idintegerOptional*Contact record ID — alternative to recipient.
message_typestringOptionalOne of: text (default), image, video, document, audio
contentstringOptional*Message text or media caption. Required unless template_id is provided.
media_urlstring (URL)ConditionalPublic HTTPS URL of the media file. Required when message_type is image, video, document, or audio.
filenamestringConditionalFilename displayed in chat (e.g. invoice.pdf). Required when message_type is image, video, document, or audio.
template_idintegerOptionalID of a saved message template. When set, content is taken from the template.
template_variablesobjectOptionalKey-value map to fill {{variable}} placeholders in the template.
delayintegerOptionalArtificial delay in milliseconds before the message is dispatched.
quotedobjectOptionalQuote/reply to an existing message. Pass {"key":{"id":"WHATSAPP_MSG_ID"}}.
link_previewbooleanOptionalEnable URL preview in text messages (default: true).
mentionedarrayOptionalArray of JIDs to @-mention, e.g. ["966501234567@s.whatsapp.net"].
mentions_everyonebooleanOptionalMention all group members (group chats only).
* Recipient: Either recipient or contact_id must be provided. * Content: Either content or template_id must be provided.

Request Examples

Text message

JSON
{
  "instance": "my-instance",
  "recipient": "+966501234567",
  "message_type": "text",
  "content": "Hello from our API!"
}

Image message

JSON
{
  "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

JSON
{
  "instance": "my-instance",
  "recipient": "+966501234567",
  "template_id": 7,
  "template_variables": {
    "name": "Ahmed",
    "order_id": "ORD-9988"
  }
}

Response (202 Accepted)

JSON
{
  "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.

POST
/integration/v1/messages/send-bulk

Required Permission

send_message

Parameters

ParameterTypeRequiredDescription
instancestringRequiredInstance ID or name used for all messages
template_idintegerOptionalShared template applied to messages that have no individual content
template_variablesobjectOptionalDefault variable values. Per-message overrides take precedence.
messagesarrayRequiredArray of 1–100 message objects (same fields as Send Message)

Request Example

JSON
{
  "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)

JSON
{
  "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" }
    ]
  }
}
Partial Success: If some messages fail (e.g. insufficient credits), the others are still queued. Check success_count and failure_count in the response.

Send Status (Story)

Broadcast a WhatsApp Status (Story) to your contacts.

POST
/integration/v1/messages/send-status

Required Permission

send_message

Parameters

ParameterTypeRequiredDescription
instancestringRequiredInstance ID or name
typestringRequiredStory type: text, image, video, or audio
contentstringRequiredText content (text type) or public media URL (image/video/audio)
captionstringOptionalCaption shown under media stories
background_colorstringOptionalHex background color for text stories, e.g. #25D366
fontintegerOptionalFont style 1–7 (text stories only)
all_contactsbooleanOptionalBroadcast to all contacts (default: true)
status_jid_listarrayOptionalSpecific contact JIDs to target (overrides all_contacts)

Request Example

JSON
{
  "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.

POST
/integration/v1/messages/send-location

Required Permission

send_message

Parameters

ParameterTypeRequiredDescription
instancestringRequiredInstance ID or name
recipientstringOptional*Phone in E.164 format
contact_idintegerOptional*Contact record ID (alternative to recipient)
latitudenumberRequiredDecimal latitude, -90 to 90
longitudenumberRequiredDecimal longitude, -180 to 180
namestringOptionalDisplay name for the pin
addressstringOptionalStreet address shown below the pin

Request Example

JSON
{
  "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.

POST
/integration/v1/messages/send-contact

Required Permission

send_message

Parameters

ParameterTypeRequiredDescription
instancestringRequiredInstance ID or name
recipientstringOptional*Phone in E.164 format
contact_idintegerOptional*Contact record ID (alternative to recipient)
contactsarrayRequiredArray of vCard contact objects (see fields below)

Contact Object Fields

FieldTypeRequiredDescription
fullNamestringRequiredContact display name
wuidstringRequiredDigits only, no + (e.g. 966501234567)
phoneNumberstringRequiredDisplay phone number (can include +)
organizationstringOptionalCompany name
emailstringOptionalEmail address
urlstringOptionalWebsite URL

Request Example

JSON
{
  "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.

POST
/integration/v1/messages/send-poll

Required Permission

send_message

Parameters

ParameterTypeRequiredDescription
instancestringRequiredInstance ID or name
recipientstringOptional*Phone in E.164 format
contact_idintegerOptional*Contact record ID (alternative to recipient)
namestringRequiredPoll question (max 255 characters)
valuesarrayRequiredArray of option strings (minimum 2, maximum 13)
selectable_countintegerOptionalMax options a user can select (default: 1)
delayintegerOptionalArtificial delay in milliseconds before dispatch
quotedobjectOptionalQuote/reply to an existing message

Request Example

JSON
{
  "instance": "my-instance",
  "recipient": "+966501234567",
  "name": "What is your preferred contact time?",
  "values": ["Morning", "Afternoon", "Evening"],
  "selectable_count": 1
}

Send Buttons

Send a message with interactive tap-to-action buttons.

POST
/integration/v1/messages/send-buttons

Required Permission

send_message

Parameters

ParameterTypeRequiredDescription
instancestringRequiredInstance ID or name
recipientstringOptional*Phone in E.164 format
contact_idintegerOptional*Contact record ID (alternative to recipient)
titlestringRequiredHeader text (max 255 characters)
descriptionstringRequiredBody text
footerstringOptionalFooter text (max 60 characters)
buttonsarrayRequiredArray of button objects (see button types below)

Button Types

typeExtra FieldDescription
replyidQuick-reply — triggers a callback with the given id
urlurlOpens a URL in the browser
callphoneNumberInitiates a phone call
copycopyCodeCopies text to the recipient's clipboard

Request Example

JSON
{
  "instance": "my-instance",
  "recipient": "+966501234567",
  "title": "Your Order is Ready",
  "description": "Order #5678 is packed and ready for pickup.",
  "footer": "Thank you for shopping with us",
  "buttons": [
    { "type": "reply", "displayText": "Confirm Pickup",  "id": "confirm_5678" },
    { "type": "url",   "displayText": "View on Map",     "url": "https://maps.google.com/?q=24.7,46.7" },
    { "type": "call",  "displayText": "Call Us",         "phoneNumber": "+966200001234" }
  ]
}

Get Message Status

Check the current delivery status of a specific sent message.

GET
/integration/v1/messages/{messageId}/status

Required Permission

send_message

Response

JSON
{
  "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

pendingQueued, not yet dispatched
processingBeing sent to WhatsApp right now
sentAccepted by WhatsApp servers
deliveredDelivered to the recipient's device
failedSend attempt failed
refundedFailed — credit returned to pocket

Get Message Detail

Retrieve full details of a single message record, including content, metadata, and all status timestamps.

GET
/integration/v1/messages/{messageId}

Required Permission

read_messages

List Messages

Get a paginated list of all messages for the authenticated account.

GET
/integration/v1/messages

Required Permission

read_messages

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger50Results per page (max 100)
statusstringFilter: pending, sent, delivered, failed
typestringFilter by message type, e.g. text, image

Response

JSON
{
  "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.

GET
/integration/v1/instances

Required Permission

manage_instances

Response

JSON
{
  "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.

GET
/integration/v1/instances/{instanceId}

Required Permission

manage_instances

Get Instance Live Status

Query the live connection status from the underlying WhatsApp gateway in real time.

GET
/integration/v1/instances/{instanceId}/status

Required Permission

manage_instances

Response

JSON
{
  "success": true,
  "data": {
    "instance_id": 124,
    "name": "my-instance",
    "connection_status": "open",
    "phone_number": "+966501234567",
    "battery": 85,
    "plugged": true
  }
}

Connection Status Values

openConnected and ready to send
closeDisconnected — scan QR to reconnect
connectingIn the process of connecting

Contact Management

List Contacts

Get all contact records for your account.

GET
/integration/v1/contacts

Required Permission

manage_contacts

Create Contact

Add a new contact. The returned id can be used as contact_id in any send endpoint.

POST
/integration/v1/contacts

Required Permission

manage_contacts

Parameters

ParameterTypeRequiredDescription
phonestringRequiredPhone in E.164 format, e.g. +966501234567
labelstringOptionalDisplay name / label for the contact
emailstringOptionalEmail address
notesstringOptionalFree-text notes about the contact

Request Body

JSON
{
  "phone": "+966501234567",
  "label": "Ahmed Al-Rashid",
  "email": "ahmed@example.com",
  "notes": "VIP customer"
}

Get Contact

Get a single contact record by ID.

GET
/integration/v1/contacts/{contactId}

Required Permission

manage_contacts

Update Contact

Update an existing contact. Only include fields you want to change.

PUT
/integration/v1/contacts/{contactId}

Required Permission

manage_contacts

Request Body

JSON
{
  "label": "Ahmed Al-Rashid (Updated)",
  "notes": "Upgraded to Premium"
}

Delete Contact

Permanently delete a contact record.

DELETE
/integration/v1/contacts/{contactId}

Required Permission

manage_contacts
Irreversible: Deleting a contact does not delete its message history, but the contact record cannot be recovered.

Template Management

Create Template

Create a reusable message template with {{variable}} placeholders. Use the returned id as template_id in send requests.

POST
/integration/v1/templates

Required Permission

manage_templates

Parameters

ParameterTypeRequiredDescription
workspace_idintegerRequiredWorkspace the template belongs to
namestringRequiredInternal name for the template
message_typestringRequiredOne of: text, image, video, document, audio
contentstringRequiredTemplate body. Use {{variable_name}} for placeholders.
is_activebooleanOptionalEnable/disable the template (default: true)

Request Example

JSON
{
  "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)

JSON
{
  "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.

GET
/integration/v1/templates

Required Permission

manage_templates

Workspace Management

List Workspaces

Get all workspaces belonging to the authenticated account.

GET
/integration/v1/workspaces

Required Permission

manage_workspaces

Get Workspace

Get full details of a single workspace.

GET
/integration/v1/workspaces/{workspaceId}

Required Permission

manage_workspaces

Disabled Endpoints

The following endpoints are permanently disabled and return 410 Gone regardless of the request body.

POST /integration/v1/messages/send-reaction — Not available in this API version.
POST /integration/v1/messages/send-list — WhatsApp has deprecated interactive list messages.

Error Handling

HTTP Status Codes

200OK — request succeeded
201Created — resource created
202Accepted — message queued for sending
400Bad Request
401Unauthorized — missing or invalid API key
402Payment Required — insufficient credits in pocket
403Forbidden — API key lacks required permission
404Not Found — resource does not exist
410Gone — endpoint is permanently disabled
422Validation Error — check the errors field
429Too Many Requests — rate limit exceeded
500Server Error

Error Response Shape

JSON — 422 Validation Error
{
  "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."]
  }
}
JSON — 402 Insufficient Credits
{
  "success": false,
  "message": "Insufficient credits: not enough balance",
  "error_code": "INSUFFICIENT_CREDITS"
}

Code Examples

Ready-to-use snippets in multiple languages.

PHP / Laravel
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'];
}
JavaScript / Node.js
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();
Python
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
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_id instead 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 402 by topping up your pocket credits
  • Handle 410 by removing calls to disabled endpoints
  • Poll /messages/{id}/status for 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