Wazzap AI API Documentation - Sending WhatsApp Messages
Table of Contents
- Introduction
- Prerequisites and API Key Generation
- Authentication
- Sending Messages
- Phone Number Format
- Immediate or Queued Sending (waitForSend)
- Disabling AI Automatic Response
- Tracking and Statistics
- Rate Limiting and Queue
- Code Examples
- Error Handling
- Best Practices
Introduction
This API allows you to send WhatsApp messages programmatically using an API key.
Main Features
- Secure authentication via API key
- Text message sending
- Rate limiting: 100 requests/minute per key (default)
- Automatic WhatsApp account detection
- Automatic phone number verification and correction (see Phone Number Format)
Prerequisites and API Key Generation
Step 1: Have a Wazzap AI Premium Account
You or your users must have an active Premium subscription on Wazzap AI.
Step 2: Create an AI Agent
- Log in to your Wazzap AI account
- Create a new AI agent from your dashboard
- Configure your agent according to your needs
Step 3: Connect Your WhatsApp Number
- From your agent's page, connect your WhatsApp number
- Scan the QR Code with WhatsApp
- Wait for the connection to be established
Step 4: Generate Your API Key
- Go to your account Settings
- Access the API Keys section
- Click on Generate a new key
- Give your key a name (e.g., "Maketou Integration")
- Select the WhatsApp agent to associate
- Copy the key immediately (it will not be displayed again)
⚠️ Important: Keep this key secure, it will only be displayed once!
Authentication
All requests require an Authorization header with Bearer format:
Authorization: Bearer wz_your_complete_api_key
Key format: wz_xxxxxxxxxxxxxxxxxxxxxx (32 characters after the prefix)
Response language
API messages (the message field, and errorMessage in /stats) are in French by default. To get them in English, add this header to your requests:
Accept-Language: en
All examples in this documentation assume Accept-Language: en. Codes (errorCode, status) never change with the language: test them in your code rather than the message text.
Sending Messages
Endpoint
POST https://api21.wazzap.ai/api/wazzap/send-message
Required Headers
Authorization: Bearer wz_your_api_key
Content-Type: application/json
Accept-Language: en
Accept-Language: en is optional: it returns API messages in English (French by default).
Request Body
{
"phoneNumber": "+33612345678",
"message": "Your message here",
"disableAiResponse": false
}
Parameters
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
phoneNumber |
string |
Yes | Recipient's number, in international format with the country code (see Phone Number Format) | "+33612345678" |
message |
string |
Yes | Text message to send | "Hello!" |
disableAiResponse |
boolean |
No | Disables the AI automatic response for this contact (default: false) |
true |
waitForSend |
boolean |
No | Wait for the actual send before responding, to get the messageId or the exact error (default: false) |
true |
simulateTyping |
boolean |
No | Shows "typing…" to the recipient before sending, duration proportional to the message (default: true) |
false |
delayMs |
number |
No | Delay before sending, in milliseconds (default: 0) |
2000 |
Success Response (200)
By default, the API responds immediately: the number has been verified and the message accepted, then sent in the background (after delayMs and the typing simulation). The messageId is therefore not known yet:
{
"success": true,
"code": 200,
"message": "Message accepted — sending in progress (typing simulation)",
"data": {
"status": "queued",
"messageId": null,
"phoneNumber": "+33612345678",
"formattedPhoneNumber": "33612345678",
"provider": "v2.5",
"agentId": "agent_123",
"organizationId": "org_456",
"aiResponseDisabled": false
}
}
With "waitForSend": true, the response arrives once the message has actually been sent, with its messageId:
{
"success": true,
"code": 200,
"message": "Message sent successfully",
"data": {
"status": "sent",
"messageId": "3EB0XXXXXXXXXXXX",
"phoneNumber": "+33612345678",
"formattedPhoneNumber": "33612345678",
"provider": "v2.5"
}
}
Error Response (4xx/5xx)
{
"success": false,
"code": 400,
"errorCode": "MISSING_PHONE_NUMBER",
"message": "The 'phoneNumber' parameter is required"
}
Phone Number Format
The right format
Always send the full number in international format, with the country code:
| Country | ✅ Expected format | ❌ Avoid |
|---|---|---|
| France | +33612345678 or 33612345678 |
0612345678 |
| Côte d'Ivoire | +2250701020304 |
0701020304 |
| Senegal | +221771234567 |
771234567 |
| Mexico | +5213112345678 |
3112345678 |
Spaces, dashes, parentheses and + are accepted and stripped automatically: "+33 6 12 34 56 78" works.
Automatic verification and correction
Before each send, Wazzap checks with WhatsApp that the number exists, then sends to the actual WhatsApp number:
- The number as provided is checked first. WhatsApp recognizes known variants: in Mexico,
523112345678is registered as5213112345678, so the message goes to the right number. 00international prefix:0033612345678is read as33612345678.- Missing country code or national format: if the number doesn't exist as provided, it is re-read in the country of the WhatsApp number connected to your agent. With an agent connected on a French number,
0612345678becomes33612345678.
⚠️ Correction #3 only works when the recipient is in the same country as your agent's WhatsApp number. For contacts abroad, the country code is required.
The formattedPhoneNumber field in the response shows the number actually used.
Number not on WhatsApp
If no variant of the number exists on WhatsApp, nothing is sent and the API responds:
{
"success": false,
"code": 400,
"errorCode": "NUMBER_NOT_ON_WHATSAPP",
"message": "This number is not on WhatsApp. Check it and use the full number with the country code (e.g. 33612345678)."
}
Check the number in your database (country code, missing digit) before retrying.
Immediate or Queued Sending (waitForSend)
waitForSend: false (default) |
waitForSend: true |
|
|---|---|---|
| Response | Immediate, status: "queued" |
After the actual send, status: "sent" |
messageId |
null |
WhatsApp message ID |
| Number not on WhatsApp | Immediate 400 error |
Immediate 400 error |
| Send fails after acceptance | Visible afterwards in /stats (FAILED) |
Error directly in the response |
| Recommended for | High-volume notifications | CRM integrations that need to know whether each message was sent |
{
"phoneNumber": "+33612345678",
"message": "Your order is ready",
"waitForSend": true
}
Response time and recommended timeout
| Mode | Usual response time | Recommended client timeout |
|---|---|---|
waitForSend: false (default) |
1 to 3 seconds | 15 seconds |
waitForSend: true |
A few more seconds (typing simulation proportional to the message, then sending) | 30 seconds, or "simulateTyping": false |
A session error (SESSION_NOT_CONNECTED) or a WhatsApp restriction (WHATSAPP_RESTRICTED) is returned immediately: no need to retry in a loop.
Disabling AI Automatic Response
What is disableAiResponse?
The disableAiResponse parameter allows you to disable the automatic conversation handling by the AI agent connected to your WhatsApp account for a specific contact. When activated (true), the contact switches to human mode, which means:
- Your message will be sent normally
- The contact will be created and linked to your agent
- The AI will NOT automatically respond to this contact's messages
- You can respond manually from your Wazzap AI dashboard or from your WhatsApp on your smartphone.
Typical Use Cases
1. VIP Support or Escalation
// Important client requiring human attention
await sendMessage(
'+3300000000',
'Hello Mr. Smith, an advisor will respond to you shortly.',
{ disableAiResponse: true }
);
2. One-way Notifications Without Expecting Response
// Delivery notification without requiring AI response
await sendMessage(
'+33698765432',
'Your package has arrived at the pickup point.',
{ disableAiResponse: true }
);
3. Personalized Marketing Campaigns
// Send a special offer without activating AI
const clients = ['+33601...', '+33602...'];
for (const client of clients) {
await sendMessage(
client,
'Exclusive offer: -20% this weekend!',
{ disableAiResponse: true }
);
}
Complete Example
const response = await fetch('https://api21.wazzap.ai/api/wazzap/send-message', {
method: 'POST',
headers: {
'Authorization': 'Bearer wz_your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
phoneNumber: '+3300000000',
message: 'Hello, a human will respond to you quickly.',
disableAiResponse: true // AI will not automatically respond
})
});
const data = await response.json();
console.log('AI Response Disabled:', data.data.aiResponseDisabled); // true
How to Reactivate AI for a Contact?
To reactivate automatic AI response for a contact:
- Log in to your Wazzap AI dashboard
- Access the contact's conversation
- Click on the "Reactivate AI" button or similar
Or simply send a new message without the disableAiResponse parameter (or with false):
await sendMessage(
'+3300000000',
'AI is now reactivated',
{ disableAiResponse: false } // AI will respond normally
);
Important to Know
- The parameter applies per contact and persists until reactivation
- A contact with
disableAiResponse: truewill be marked as "Human mode" in your dashboard - You can always respond manually via the dashboard
- AI remains active for all other contacts
Tracking and Statistics
Statistics Endpoint
Check your API key's performance in real-time:
GET https://api21.wazzap.ai/api/wazzap/stats
Authorization: Bearer wz_your_api_key
Response
{
"success": true,
"data": {
"apiKey": {
"id": "cm9xxx...",
"name": "Production API",
"agentId": "cm9yyy..."
},
"stats": {
"totalRequests": 1250,
"successRequests": 1200,
"failedRequests": 50,
"successRate": 96.0
},
"queue": {
"processing": 2,
"pending": 5,
"maxConcurrent": 4
},
"rateLimit": {
"limit": 100,
"current": 45,
"remaining": 55,
"resetAt": "2025-10-15T15:30:00.000Z"
},
"recentRequests": [
{
"id": "req_xxx",
"status": "COMPLETED",
"statusCode": 200,
"duration": 234,
"provider": "v2.5",
"errorMessage": null,
"startedAt": "2025-10-15T15:25:00.000Z",
"completedAt": "2025-10-15T15:25:00.234Z"
}
// ... 9 other recent requests
]
}
}
Available Metrics
| Metric | Description |
|---|---|
totalRequests |
Total number of requests made |
successRequests |
Number of successful requests |
failedRequests |
Number of failed requests |
successRate |
Success rate as percentage |
queue.processing |
Number of requests being processed |
queue.pending |
Number of requests waiting |
rateLimit.current |
Number of requests in current window |
rateLimit.remaining |
Number of requests remaining before limit |
recentRequests[].status |
COMPLETED = message sent, FAILED = message not sent |
recentRequests[].errorMessage |
Reason for the failure when status is FAILED (e.g. number not on WhatsApp) |
ℹ️ With a
queuedsend (default), the request is first recorded as accepted. If the background send then fails, it switches toFAILEDin/stats, with the reason inerrorMessage.
Monitoring Example
async function monitorAPI() {
const response = await fetch('https://api21.wazzap.ai/api/wazzap/stats', {
headers: {
'Authorization': 'Bearer wz_your_api_key'
}
});
const { data } = await response.json();
console.log(`Success rate: ${data.stats.successRate}%`);
console.log(`Queue: ${data.queue.processing}/${data.queue.maxConcurrent} processing, ${data.queue.pending} pending`);
console.log(`Rate limit: ${data.rateLimit.remaining}/${data.rateLimit.limit} remaining`);
// Alert if issue detected
if (data.stats.successRate < 90) {
console.warn('Low success rate - check WhatsApp connection');
}
}
// Monitor every 30 seconds
setInterval(monitorAPI, 30000);
Rate Limiting and Queue
Automatic Queue System
To ensure stability, each API key has an intelligent queue that processes maximum 4 requests in parallel.
How does it work?
Your requests → [Queue] → [4 processing slots] → WhatsApp Sending
↑
Maximum 4 simultaneous
Others wait
Rate Limiting
Each API key has configurable limits:
| Parameter | Default | Description |
|---|---|---|
rateLimit |
100 | Requests per window |
rateLimitWindow |
60s | Window duration |
By default: 100 requests per minute
Handling Exceeded Limits
If you exceed the limit:
{
"success": false,
"code": 429,
"errorCode": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please wait.",
"rateLimit": {
"limit": 100,
"remaining": 0,
"reset": "2025-10-15T15:30:00.000Z"
}
}
Best Practices to Avoid Rate Limiting
// 1. Check before bulk sending
async function sendIfPossible(phoneNumber, message) {
const stats = await getStats();
if (stats.data.rateLimit.remaining < 5) {
const resetTime = new Date(stats.data.rateLimit.resetAt);
const waitTime = resetTime.getTime() - Date.now();
console.log(`Waiting ${Math.ceil(waitTime/1000)}s...`);
await new Promise(r => setTimeout(r, waitTime));
}
return await sendMessage(phoneNumber, message);
}
// 2. Automatically space out sends
async function sendWithSpacing(contacts, intervalMs = 700) {
for (const contact of contacts) {
await sendMessage(contact.phone, contact.message);
await new Promise(r => setTimeout(r, intervalMs));
}
}
Increasing Your Rate Limit
To increase your rate limit, contact your administrator or upgrade to a higher plan.
Code Examples
Simple Message Sending
Error Handling
Error Codes
| HTTP Code | Error Code | Description | Solution |
|---|---|---|---|
200 |
- | Success | - |
400 |
MISSING_PHONE_NUMBER |
Missing number | Add phoneNumber |
400 |
MISSING_MESSAGE |
Missing message | Add message |
400 |
NUMBER_NOT_ON_WHATSAPP |
The number doesn't exist on WhatsApp, even after correction | Check the number and its country code |
400 |
INVALID_DELAY |
delayMs is not a positive number |
Fix delayMs |
400 |
NO_WHATSAPP_PROVIDER |
No WhatsApp connected | Connect WhatsApp |
401 |
API_KEY_MISSING |
Missing key | Add Authorization header |
401 |
API_KEY_INVALID_FORMAT / API_KEY_INVALID_PREFIX |
Malformed key | Copy the full key (wz_…) |
401 |
API_KEY_INVALID_OR_EXPIRED |
Invalid key | Verify key |
403 |
NO_WHATSAPP_ACCOUNT_CONNECTED |
WhatsApp not connected | Reconnect WhatsApp |
403 |
WHATSAPP_RESTRICTED |
WhatsApp has temporarily restricted the number (anti-spam protection) | Wait a few hours, avoid bulk sends to new contacts |
409 |
SESSION_NOT_CONNECTED |
The agent's WhatsApp session got disconnected | Re-scan the QR code in Wazzap AI |
404 |
AGENT_NOT_FOUND |
Agent not found | Verify agent |
429 |
RATE_LIMIT_EXCEEDED |
Too many requests | Wait until the window resets (rateLimit.reset) |
500 |
MESSAGE_SEND_ERROR |
Server error | Retry |
Error Examples
Missing API Key
{
"success": false,
"code": 401,
"errorCode": "API_KEY_MISSING",
"message": "API key missing. Use the 'Authorization: Bearer YOUR_API_KEY' header"
}
Missing Phone Number
{
"success": false,
"code": 400,
"errorCode": "MISSING_PHONE_NUMBER",
"message": "The 'phoneNumber' parameter is required"
}
Number Not on WhatsApp
{
"success": false,
"code": 400,
"errorCode": "NUMBER_NOT_ON_WHATSAPP",
"message": "This number is not on WhatsApp. Check it and use the full number with the country code (e.g. 33612345678)."
}
WhatsApp Account Not Connected
{
"success": false,
"code": 403,
"errorCode": "NO_WHATSAPP_ACCOUNT_CONNECTED",
"message": "No WhatsApp account connected for this agent"
}
Best Practices
1. Security
Do:
- Store the key in environment variables
- Never expose the key on client-side
- Use HTTPS only
- Regenerate the key if compromised
Don't:
- Hardcode the key in code
- Commit the key to Git
- Share the key publicly
Secure Example:
// .env
WAZZAP_AI_API_KEY=wz_your_api_key
WAZZAP_AI_API_URL=https://api21.wazzap.ai
// config.js
require('dotenv').config();
module.exports = {
apiKey: process.env.WAZZAP_AI_API_KEY,
apiUrl: process.env.WAZZAP_AI_API_URL
};
2. Rate Limiting
- Default: 100 requests per minute per API key
- Implement a queue for bulk sends
3. Data Validation
// Normalize the number to international format (E.164)
function validateNumber(phoneNumber) {
let cleaned = phoneNumber.replace(/[^\d+]/g, '');
if (cleaned.startsWith('00')) cleaned = '+' + cleaned.slice(2);
if (!/^\+?\d{8,15}$/.test(cleaned)) {
throw new Error('Invalid number');
}
// A number starting with 0 is in national format: add the country code
if (/^0\d/.test(cleaned)) {
throw new Error('Missing country code (e.g. +33 for France)');
}
return cleaned.startsWith('+') ? cleaned : '+' + cleaned;
}
// Handle the "number not on WhatsApp" case
const result = await sendMessage(number, message);
if (result.errorCode === 'NUMBER_NOT_ON_WHATSAPP') {
// Flag the contact as "no WhatsApp" in your CRM
}
// Validate message
function validateMessage(message) {
if (!message || typeof message !== 'string') {
throw new Error('Invalid message');
}
if (message.length > 4096) {
throw new Error('Message too long (max 4096 characters)');
}
return message.trim();
}
📚 Use Cases
Sending Personalized Notifications
const clients = [
{ name: 'John Smith', phone: '+3300000000', order: '#12345' },
{ name: 'Mary Johnson', phone: '+33698765432', order: '#12346' }
];
async function sendNotifications(clients) {
for (const client of clients) {
const message = `
Hello ${client.name},
Your order ${client.order} has been confirmed.
Thank you!
`.trim();
await sendMessage(client.phone, message);
await new Promise(r => setTimeout(r, 3000)); // Wait 3s
}
}
Support
Common Issues
Message doesn't arrive:
- Check that the number is in international format with the country code (see Phone Number Format)
- Check
/stats: aFAILEDrequest gives the reason inerrorMessage - Use
"waitForSend": trueto get the send result directly in the response - Check that your agent's WhatsApp number is connected (version 2.5) in Wazzap AI
"Rate limit exceeded":
- Reduce sending frequency
- Wait 1 minute
- Implement a queue
Invalid API key:
- Verify you copied the complete key
- Verify the key hasn't expired
- Regenerate a new key if necessary
Contact
For any questions:
- First consult this documentation
- Contact us at contact@wazzap.ai
