WhatsApp MCP Server API Reference
API documentation for all 34 MCP tools
Table of Contents
- Authentication & Status
- Messaging
- Contacts & Chats
- Media
- Intelligence
- Approval Workflows
- Groups
- Message Actions
- Contact Info
- Workflow
- Tool Documentation
Authentication & Status
disconnect
Log out and disconnect from WhatsApp. Clears the current session so the device is unlinked.
Parameters: None
Returns:
{
content: [{
type: 'text';
text: string; // Confirmation message
}];
isError?: boolean;
}
Example:
disconnect()
Notes:
- After disconnecting, you will need to call
authenticateagain to re-link the device. - The stored messages and media are not deleted — only the session credentials are cleared.
authenticate
Link device to WhatsApp using 8-digit pairing code or QR code.
Parameters:
{
phoneNumber?: string; // E.164 format: "+1234567890" (required when not yet linked)
waitForLink?: boolean; // Wait for device to link (default: true)
linkTimeoutSec?: number; // Max wait time: 15-600 seconds (default: 120)
pollIntervalSec?: number; // Poll interval: 2-60 seconds (default: 5)
}
Returns:
{
content: [{
type: 'text' | 'image';
text: string;
data?: string; // Base64 image (for QR)
mimeType?: string; // 'image/png' (for QR)
}];
isError?: boolean;
}
Example:
authenticate({ phoneNumber: "+15145551234" })
Error Codes:
429- Rate limited (5 attempts per 30 minutes)400- Invalid phone number format408- Pairing code expired (60 seconds)
get_connection_status
Check WhatsApp connection state and database statistics.
Parameters: None
Returns:
{
content: [{
type: 'text';
text: string; // Formatted status report
}];
}
Status Report Includes:
- Connection state (connected/disconnected)
- Authenticated JID
- Uptime (if connected)
- Logout reason (if disconnected)
- Database statistics (chats, messages, unread, approvals)
- Recent error count
Messaging
send_message
Send text message with fuzzy contact/group name matching.
Parameters:
{
to: string; // Name, phone, or JID
message: string; // Max 4096 characters
}
Returns:
{
content: [{
type: 'text';
text: string; // Confirmation with message ID
}];
isError?: boolean;
}
Example:
send_message({ to: "John Smith", message: "Hello!" })
Error Codes:
429- Rate limited (60 messages/minute)404- Recipient not found409- Ambiguous recipient (multiple matches)403- Contact not whitelisted
list_messages
Get messages from a specific chat with pagination and date filtering.
Parameters:
{
chat: string; // Chat name, phone, or JID
limit?: number; // Max results: 1-200 (default: 50)
page?: number; // Page number (default: 0)
before?: string; // ISO 8601 or natural date
after?: string; // ISO 8601 or natural date
include_context?: boolean; // Include surrounding messages (default: false)
context_messages?: number; // Context count (default: 2)
}
Returns:
{
content: [{
type: 'text';
text: string; // Formatted message list
}];
}
Example:
list_messages({
chat: "Engineering Group",
limit: 20,
after: "2026-03-01",
include_context: true
})
search_messages
Full-text search across all messages using SQLite FTS5.
Parameters:
{
query: string; // Search keywords (max 500 chars)
chat?: string; // Scope to specific chat
limit?: number; // Max results: 1-100 (default: 20)
page?: number; // Page number (default: 0)
include_context?: boolean; // Include context (default: false)
}
Returns:
{
content: [{
type: 'text';
text: string; // Formatted search results
}];
isError?: boolean;
}
Search Syntax:
- Keywords:
deadline project - Exact phrase:
"project deadline" - Boolean:
deadline AND urgent - Exclusion:
meeting NOT zoom
Contacts & Chats
list_chats
List conversations sorted by recent activity.
Parameters:
{
filter?: string; // Filter by name (substring)
groups_only?: boolean;// Only group chats (default: false)
limit?: number; // Max results: 1-100 (default: 20)
page?: number; // Page number (default: 0)
}
Returns:
{
content: [{
type: 'text';
text: string; // Formatted chat list
}];
}
Example:
list_chats({ groups_only: true, limit: 10 })
search_contacts
Find contacts/groups by name or phone number.
Parameters:
{
query: string; // Search term (max 500 chars)
include_chats?: boolean; // Return chats involving match (default: false)
limit?: number; // Max results: 1-50 (default: 20)
}
Returns:
{
content: [{
type: 'text';
text: string; // Formatted contact list
}];
}
Example:
search_contacts({ query: "John", include_chats: true })
Media
download_media
Download media from a received message to persistent storage.
Parameters:
{
message_id: string; // Message ID from list_messages
chat?: string; // Chat name/phone/JID (optional, for context only — lookup is by message_id)
}
Returns:
{
content: [{
type: 'text';
text: string; // Download path and metadata
}];
isError?: boolean;
}
Example:
download_media({ message_id: "msg-abc123", chat: "John" })
Error Codes:
429- Rate limited (30 downloads/minute)404- Media not found or expired413- Media quota exceeded (512 MB)
send_file
Send image, video, audio, or document with optional caption.
Parameters:
{
to: string; // Recipient name/phone/JID
file_path: string; // Absolute path in container
media_type: 'image' | 'video' | 'audio' | 'document';
caption?: string; // Max 1024 characters
}
Returns:
{
content: [{
type: 'text';
text: string; // Confirmation with message ID
}];
isError?: boolean;
}
Allowed Directories:
/data/sessions/media//tmp
Security Checks:
- Path traversal prevention
- Dangerous extension blocklist
- Magic bytes verification
- File size limit (64 MB)
Intelligence
catch_up
Get intelligent summary of recent WhatsApp activity.
Parameters:
{
since?: '1h' | '4h' | 'today' | '24h' | 'this_week';
}
Returns:
{
content: [{
type: 'text';
text: string; // Activity summary
}];
}
Summary Includes:
- Active chats with unread counts
- Questions awaiting response
- Recent unread highlights
- Pending approval requests
mark_messages_read
Mark messages as read to clear unread indicators.
Parameters:
{
chat?: string; // Chat to mark all messages
message_ids?: string[] // Specific message IDs (max 500)
}
Returns:
{
content: [{
type: 'text';
text: string; // Count of marked messages
}];
isError?: boolean;
}
Example:
mark_messages_read({ chat: "John" })
mark_messages_read({ message_ids: ["msg-1", "msg-2"] })
export_chat_data
Export complete chat history for a specific contact or group. Supports JSON and CSV formats. Designed for PIPEDA individual access rights compliance.
Parameters:
{
jid: string; // Chat JID to export (use list_chats to find JIDs)
format?: 'json' | 'csv'; // Export format (default: 'json')
}
Returns:
{
content: [{
type: 'text';
text: string; // Export confirmation with metadata
}];
isError?: boolean;
}
Example:
// Export chat to JSON
export_chat_data({ jid: "1234567890@s.whatsapp.net", format: "json" })
// Export chat to CSV
export_chat_data({ jid: "1234567890@s.whatsapp.net", format: "csv" })
Response Includes:
- Chat name and JID
- Message count
- Export format
- Export timestamp
- Preview of data (first 500 characters for CSV)
Limitations:
- Export limited to 10,000 most recent messages per call
- JSON format returns metadata only in response (full data available via programmatic access)
- CSV format includes preview in response
Compliance Use Cases:
- PIPEDA individual access requests (Canada)
- Quebec Law 25 data portability
- Personal data export for users
Approval Workflows
request_approval
Send approval request via WhatsApp; recipient replies APPROVE/DENY.
Parameters:
{
to: string; // Recipient name/phone/JID
action: string; // What needs approval (max 500 chars)
details: string; // Context (max 2000 chars)
timeout?: number; // Timeout: 10-3600 seconds (default: 300)
}
Returns:
{
content: [{
type: 'text';
text: string; // Request ID and expiry
}];
isError?: boolean;
}
Example:
request_approval({
to: "Sarah",
action: "Deploy v2.1 to production",
details: "Critical security fixes included",
timeout: 600
})
Recipient Response:
- APPROVE/YES/OK/✅ → Approved
- DENY/NO/❌ → Denied
- Reply can include request ID for clarity
check_approvals
Check status of approval requests.
Parameters:
{
request_id?: string; // Specific approval ID (omit for all pending)
}
Returns:
{
content: [{
type: 'text';
text: string; // Approval status
}];
}
Example:
check_approvals() // List all pending
check_approvals({ request_id: "approval_123_abc" }) // Specific approval
Status Values:
pending- Awaiting responseapproved- Approved by recipientdenied- Denied by recipientexpired- Timeout reached
Groups
create_group
Create a new WhatsApp group with the given name and participant phone numbers or JIDs.
Parameters:
{
name: string; // Group name (1–100 characters)
participants: string[]; // E.164 phone numbers or JIDs (1–256 participants)
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Group JID and invite link
isError?: boolean;
}
Example:
create_group({ name: "Project Alpha", participants: ["+15145551234", "+447911123456"] })
get_group_info
Get detailed information about a WhatsApp group: name, description, participants, admin list, and settings.
Parameters:
{
group: string; // Group name (fuzzy match) or JID ending in @g.us
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Name, JID, description, participant list with roles
}
Example:
get_group_info({ group: "Engineering Team" })
get_joined_groups
List all WhatsApp groups this account is a member of, with participant counts and admin status.
Parameters: None
Returns:
{
content: [{ type: 'text'; text: string }]; // Formatted list with names, member counts, JIDs
}
get_group_invite_link
Get the invite link for a WhatsApp group. Anyone with the link can join. Requires admin privileges.
Parameters:
{
group: string; // Group name (fuzzy match) or JID ending in @g.us
}
Returns:
{
content: [{ type: 'text'; text: string }]; // https://chat.whatsapp.com/CODE
}
join_group
Join a WhatsApp group using an invite link or invite code.
Parameters:
{
link: string; // Full invite URL (https://chat.whatsapp.com/CODE) or just the code
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Joined group JID
isError?: boolean;
}
leave_group
Leave a WhatsApp group. This action is permanent — you will need an invite to rejoin.
Parameters:
{
group: string; // Group name (fuzzy match) or JID ending in @g.us
}
Returns:
{
content: [{ type: 'text'; text: string }];
isError?: boolean;
}
update_group_participants
Add, remove, promote to admin, or demote participants in a WhatsApp group. Requires admin privileges for most actions.
Parameters:
{
group: string; // Group name (fuzzy match) or JID ending in @g.us
action: 'add' | 'remove' | 'promote' | 'demote';
participants: string[]; // E.164 phone numbers or JIDs (1–50 participants)
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Per-participant outcome
isError?: boolean;
}
Example:
update_group_participants({
group: "Engineering Team",
action: "promote",
participants: ["+15145551234"]
})
set_group_name
Change the name of a WhatsApp group. Requires admin privileges.
Parameters:
{
group: string; // Group name (fuzzy match) or JID ending in @g.us
name: string; // New group name (1–100 characters)
}
Returns:
{
content: [{ type: 'text'; text: string }];
isError?: boolean;
}
set_group_topic
Set or update the description/topic of a WhatsApp group. Requires admin privileges.
Parameters:
{
group: string; // Group name (fuzzy match) or JID ending in @g.us
topic: string; // New description (max 512 chars; empty string to clear)
}
Returns:
{
content: [{ type: 'text'; text: string }];
isError?: boolean;
}
Message Actions
send_reaction
React to a WhatsApp message with an emoji. Use an empty string to remove an existing reaction.
Parameters:
{
chat: string; // Chat name, phone number, or JID
message_id: string; // Message ID (from list_messages output)
emoji: string; // Emoji to react with (e.g. "👍"). Empty string removes reaction.
}
Returns:
{
content: [{ type: 'text'; text: string }];
isError?: boolean;
}
Example:
send_reaction({ chat: "John", message_id: "msg-abc123", emoji: "👍" })
send_reaction({ chat: "John", message_id: "msg-abc123", emoji: "" }) // remove
edit_message
Edit a previously sent WhatsApp message. Only works on messages sent by this account within the last ~15 minutes.
Parameters:
{
chat: string; // Chat name, phone number, or JID
message_id: string; // Message ID to edit (from list_messages output)
new_text: string; // Replacement text (max 4096 characters)
}
Returns:
{
content: [{ type: 'text'; text: string }];
isError?: boolean;
}
Notes:
- Only messages sent by this account can be edited
- WhatsApp enforces a ~15-minute edit window
- Edited messages show an “edited” indicator to recipients
delete_message
Delete a WhatsApp message for everyone in the chat (revoke). Only works on messages sent by this account.
Parameters:
{
chat: string; // Chat name, phone number, or JID
message_id: string; // Message ID to delete (from list_messages output)
}
Returns:
{
content: [{ type: 'text'; text: string }];
isError?: boolean;
}
Notes:
- Message is removed for all participants
- Only messages sent by this account can be deleted
destructiveHint: true— this action cannot be undone
Contact Info
get_user_info
Get WhatsApp profile information for one or more phone numbers: display name, status, and business details.
Parameters:
{
phones: string[]; // Phone numbers in E.164 format (1–20 numbers)
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Per-number: name, status, business flag
isError?: boolean;
}
Example:
get_user_info({ phones: ["+15145551234", "+447911123456"] })
is_on_whatsapp
Check whether one or more phone numbers have WhatsApp accounts. Useful before sending a message to a new contact.
Parameters:
{
phones: string[]; // Phone numbers in E.164 format (1–50 numbers)
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Per-number: ✅ on WhatsApp / ❌ not on WhatsApp
}
Example:
is_on_whatsapp({ phones: ["+15145551234"] })
get_profile_picture
Get the profile picture URL for a contact or group. Returns the direct image URL from WhatsApp’s CDN.
Parameters:
{
target: string; // Phone number, contact name, group name, or JID
}
Returns:
{
content: [{ type: 'text'; text: string }]; // CDN URL or "no profile picture set"
isError?: boolean;
}
Notes:
- CDN URLs expire after a short period; fetch the image promptly
- Privacy settings may prevent retrieval for non-contacts
sync_contact_names
Fetch WhatsApp profile names and store them locally for chats that still show as raw JIDs. Syncs all such contacts, or specific JIDs/phone numbers. Does not override custom names set via set_contact_name.
Parameters:
{
contacts?: string[]; // JIDs or E.164 phones to sync; omit to sync all with no display name
force?: boolean; // Re-fetch even when a name is already stored (default false)
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Summary of names synced and skipped
isError?: boolean;
}
Notes:
- Custom names (set via
set_contact_name) are never overwritten - Requires an active WhatsApp connection
set_contact_name
Set a custom display name for a contact or group JID, stored locally. This name takes priority over push names in list_chats, search_contacts, and catch_up. Pass an empty string to clear a previously set name.
Parameters:
{
jid: string; // JID (e.g. 123@s.whatsapp.net) or E.164 phone number
name: string; // Display name, or empty string to remove the custom name
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Confirmation or cleared message
isError?: boolean;
}
Notes:
- Custom names are stored locally in SQLite; WhatsApp contacts are not notified
- Takes highest priority in name resolution (above push names and JIDs)
Workflow
wait_for_message
Block until an incoming WhatsApp message arrives, then return it. Use during interactive AI workflows: ask the user to send a message, call this tool, and the AI receives the reply automatically.
Parameters:
{
timeout?: number; // Seconds to wait (1–300, default: 60)
chat?: string; // Only match messages from this chat (name, phone, or JID)
from_phone?: string; // Only match messages from this sender phone or JID
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Sender, chat, timestamp, body, media info, message ID
isError?: boolean; // isError: true if timeout reached
}
Example:
// Wait up to 2 minutes for any message from John
wait_for_message({ timeout: 120, chat: "John" })
// Wait for a message from a specific number
wait_for_message({ timeout: 60, from_phone: "+15145551234" })
Notes:
- Returns
isError: truewith a descriptive message if the timeout expires with no match readOnlyHint: true— does not send anything; only observes incoming messages
Tool Documentation
get_tool_info
Get detailed documentation for any WhatsApp MCP tool on demand. This is the meta-tool used by the layered documentation approach.
Parameters:
{
tool_name: string; // Tool name, e.g. "send_message", "download_media"
}
Returns:
{
content: [{ type: 'text'; text: string }]; // Structured docs: usage, examples, response, errors, related tools, pitfalls
isError?: boolean;
}
Example:
get_tool_info({ tool_name: "download_media" })
Notes:
- Returns
isError: trueif the tool name is unknown. - Most tool descriptions now include a direct hint to call this tool.
MCP Notifications
The server sends async notifications for real-time events:
notifications/message_received
{
"method": "notifications/message_received",
"params": {
"messageId": "msg_12345",
"from": "1234567890@g.us",
"senderName": "John Doe",
"timestamp": 1711900000
}
}
Note:
fromis the chat JID (e.g. group JID ending in@g.usor contact JID ending in@s.whatsapp.net), not the individual sender’s JID. UsesenderNameto identify who sent the message.
notifications/disconnected
{
"method": "notifications/disconnected",
"params": {
"reason": "connection_lost",
"permanent": false,
"message": "WhatsApp temporarily disconnected..."
}
}
notifications/audit_failure
{
"method": "notifications/audit_failure",
"params": {
"type": "audit_failure",
"reason": "audit_db_init_failed",
"error": "Database unavailable",
"timestamp": "2026-03-31T12:00:00.000Z"
}
}
Known Limitations — Disabled Poll Tools
The following four tools exist in the codebase but are disabled in this Docker deployment due to WhatsApp’s multidevice protocol restrictions. They will return a “tool not found” error if called.
| Tool | Reason disabled |
|---|---|
create_poll |
Poll creation is functional, but vote tracking is unreliable on secondary devices — see details below |
get_poll_results |
WhatsApp does not forward poll_update_message events to companion devices in real-time |
debug_poll_votes |
Same underlying limitation as get_poll_results |
list_polls |
Read-only poll listing; disabled alongside the other poll tools for consistency |
Details: See POLL_LIMITATIONS.md for a full technical explanation, including why WhatsApp’s event-driven multidevice architecture prevents reliable vote tracking on secondary devices, and what workarounds were considered and rejected.
User guidance when asked about polls:
- Poll results shown by this server may be incomplete or show 0 votes
- Check the poll directly in the WhatsApp app for accurate, real-time vote counts
- The server must be running continuously when votes are cast to have any chance of receiving them
Error Handling
All tools return structured errors with recovery hints:
{
content: [{
type: 'text';
text: string; // Error message with hints
}];
isError: true;
}
Common Error Patterns:
- Rate limit errors include retry-after time
- Ambiguous recipient errors include candidate list
- Connection errors suggest authentication
- Permission errors list allowed contacts
Version: 2026.1
Last Updated: April 1, 2026