wuapi MCP server
OfficialAn MCP server for the wuapi WhatsApp API that lets MCP clients operate a wuapi account: link and manage numbers, send and read WhatsApp data, and administer groups, channels, webhooks, projects, invitations, branding, and usage.
Link and manage WhatsApp numbers/accounts: create accounts, get QR or pairing codes, reconnect, set presence, disappearing timers, reject calls, update, log out, or delete accounts.
Send and interact with messages: text, media/files, locations, contact cards, polls, buttons, replies, reactions, edits, deletes, cancels, forwards, stars, poll votes, and file uploads.
Read and manage chats, messages, contacts, and labels: list/get messages and chats, mark read, archive, pin, mute, delete chats, manage labels, lookup/check/save contacts, and manage block lists.
Manage profile and privacy: update profile, set/delete profile picture, get/reset contact link, manage favorite stickers, and privacy settings.
Manage groups and communities: create groups, add/remove/promote/demote participants, invite links, leave groups, group settings, join requests, and community linking/membership.
Manage channels: list/get/preview channels, read posts, create/follow/unfollow/mute/unmute, react, and mark posts viewed.
Post, read, and manage stories: view contacts' stories, list own stories, get viewers, react, reply, and delete stories.
Manage webhooks and incoming webhooks: create/update/delete/list/get event webhook endpoints and secret incoming webhook URLs without exposing secrets.
Manage organization-level resources: projects, project API key auditing/revocation, invitations and branding, and usage reports.
Exposes MCP resources for the OpenAPI spec, wuapi docs, and webhook event types, plus prompts for sending messages, setting up webhooks, inviting customers, and using Streams.
Supports local stdio and hosted HTTP connections, with optional read-only mode that registers only reading tools.
Provides tools for interacting with the WhatsApp API (wuapi) to send messages, manage chats, groups, contacts, media, and webhooks.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@wuapi MCP serversend a WhatsApp message to +15551234567 saying 'Hey, meeting moved to 3pm'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
wuapi MCP server
A Model Context Protocol server for wuapi, the WhatsApp API for developers. It lets Claude, Cursor, VS Code and other MCP clients use your wuapi account: send messages, link numbers, read conversations, manage groups, communities, channels, labels, profile and privacy, webhooks, projects and invitations.
Every tool is a call to the wuapi REST API with your own API key, through @wuapidev/sdk. The API decides what the key may do, rate limits it (600 requests per minute per key) and logs each call in your dashboard, exactly as for any other client.
Docs: wuapi.dev/docs/mcp.
How it works. wuapi links your own numbers as devices, the same way WhatsApp Web works. It does not use the WhatsApp Business Platform. WhatsApp can restrict numbers that behave like spam: send only to people who expect your messages.
Two ways to connect
Local (stdio) | Hosted (Streamable HTTP) | |
Runs | on your machine: | at |
Key | your |
|
Needs | Node 20 or later | a client that sends custom headers |
The quickest local setup uses the wuapi CLI: it logs you in from the browser, stores a key on your machine (~/.config/wuapi/credentials.json, readable only by you) and registers the server with your client, with no key in the client's config:
npx @wuapidev/cli login
npx @wuapidev/cli mcp add # Claude Code, Cursor or VS CodeThe local server uses WUAPI_API_KEY when the client sets it, and otherwise that stored login: the current profile, or the one WUAPI_PROFILE names (npx @wuapidev/cli profiles lists them). Or create an API key yourself at wuapi.dev/app/api-keys and pass it as below. A project key limits the server to one project.
Claude Code
# Local, with the key from `npx @wuapidev/cli login`
claude mcp add wuapi -- npx -y @wuapidev/mcp
# Local, with a key in the config
claude mcp add wuapi --env WUAPI_API_KEY=wu_live_... -- npx -y @wuapidev/mcp
# Hosted
claude mcp add --transport http wuapi https://wuapi.dev/api/mcp --header "Authorization: Bearer $WUAPI_API_KEY"Claude Desktop
Settings > Developer > Edit Config, then add to claude_desktop_config.json:
{
"mcpServers": {
"wuapi": {
"command": "npx",
"args": ["-y", "@wuapidev/mcp"],
"env": { "WUAPI_API_KEY": "wu_live_..." }
}
}
}Cursor
~/.cursor/mcp.json (or .cursor/mcp.json in a project):
{
"mcpServers": {
"wuapi": {
"command": "npx",
"args": ["-y", "@wuapidev/mcp"],
"env": { "WUAPI_API_KEY": "wu_live_..." }
}
}
}Or the hosted server:
{
"mcpServers": {
"wuapi": {
"url": "https://wuapi.dev/api/mcp",
"headers": { "Authorization": "Bearer wu_live_..." }
}
}
}VS Code
.vscode/mcp.json. VS Code asks for the key once and stores it outside the file:
{
"inputs": [{ "type": "promptString", "id": "wuapi-key", "description": "wuapi API key", "password": true }],
"servers": {
"wuapi": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@wuapidev/mcp"],
"env": { "WUAPI_API_KEY": "${input:wuapi-key}" }
}
}
}For the hosted server use "type": "http", "url": "https://wuapi.dev/api/mcp" and "headers": { "Authorization": "Bearer ${input:wuapi-key}" }.
Other clients
Any client that starts a local command works with npx -y @wuapidev/mcp, with WUAPI_API_KEY in its environment or after npx @wuapidev/cli login. Any client that connects to a remote server with a custom header works with the hosted endpoint. Clients that only connect to remote servers through OAuth, as some chat apps do, cannot use the hosted endpoint yet.
Related MCP server: WhatsApp MCP Stream
Configuration
Variable | |
| Your API key, |
| Which stored login to use ( |
| Act inside one project: its id or |
| API base URL. Default |
|
|
The hosted endpoint takes the same options as headers: Wuapi-Project: <id or ext:externalId> and Wuapi-Read-Only: true.
Tools
87 tools reach every operation of the REST API, except the two that return a secret and the two steps of an upload through an upload URL (see below). Tools that read are marked readOnlyHint. Tools that delete, revoke, reset, block or leave are marked destructiveHint and take confirm: true, which the model has to set on purpose and your client shows you before the call.
Area | Tools |
Context |
|
Accounts |
|
Messages |
|
Chats |
|
Contacts |
|
Profile |
|
Groups |
|
Channels |
|
Stories |
|
Webhooks |
|
Incoming webhooks |
|
Projects |
|
Invitations |
|
Usage |
|
Resources with many small operations are one tool with an action argument, so the list stays short enough for a model to pick from. The tool's description lists its actions, and each field says which actions use it. Only the destructive actions (marked below) need confirm: true.
Tool | Actions |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* needs confirm: true.
To post to a channel, use send_text or send_media with to set to the channel id. In read-only mode a tool that mixes reads and writes keeps only its reading actions (manage_channel keeps list, get, preview_invite and list_messages).
get_account_qr_code returns the QR code as an image your client can show. Projects, invitations, branding and usage need an organization key.
Not exposed: creating a project API key, rotating a webhook endpoint's signing secret and rotating an incoming webhook's URL. Both return a secret exactly once, and tool results never carry secrets, so a tool would create a key or secret nobody could read (and a rotation would break your server's signature check at once). Do both in the dashboard or with the SDK. Also not exposed: completing and reading an upload. They belong to the upload-URL flow, where the file's raw bytes are posted to a URL, which a tool cannot do; upload_file takes the bytes in its one call and returns the upload ready to send. Sending a test webhook event and reading request logs have no public API endpoint; they are in the dashboard.
src/operations.ts maps every API operation to the tools that call it; a test keeps it equal to the OpenAPI spec.
Resources and prompts
Resources: https://wuapi.dev/openapi.json (the OpenAPI 3.1 spec), https://wuapi.dev/llms-full.txt (the docs as Markdown), https://wuapi.dev/llms.txt (their index) and wuapi://webhook-events (every event type).
Prompts: send_message (to, message), setup_webhook (url, events), invite_customer (customer, externalId, email) and use_streams (project, language). use_streams helps you receive events with Streams, the live alternative to Webhooks for code with no public endpoint: with the SDK's events.stream in TypeScript and Rust, and the rules of a client in any other language.
Security
The key stays where you put it: in the client's configuration, a header, or the CLI's credentials file (mode 0600), which the server only reads. The server never logs it, never returns it, and never includes it in an error.
Tool results never contain secrets. A webhook endpoint's signing secret is returned by the API only once, when the endpoint is created;
create_webhookdrops it and tells you to reveal it in the dashboard instead. An incoming webhook's URL is a credential too: its tools drop it, and the user copies it from the dashboard. There is no tool that creates API keys.Scope the key: a project key reaches one project, and
WUAPI_MCP_READ_ONLY=trueremoves every tool that writes.Media is fetched by the wuapi API, never by this server. The API refuses private and internal addresses.
Every argument is validated against the tool's JSON schema before any request. A base URL that is not https is refused, so the key never travels in clear text.
Use it as a library
import { Wuapi } from "@wuapidev/sdk";
import { createWuapiMcpServer } from "@wuapidev/mcp";
import { createWuapiMcpHttpHandler } from "@wuapidev/mcp/http";
// One server per client connection, acting as this key.
const server = createWuapiMcpServer({ client: new Wuapi({ apiKey: process.env.WUAPI_API_KEY }) });
// Or a stateless Streamable HTTP handler: (Request) => Promise<Response>,
// authenticated with each caller's own key as a bearer token.
const handle = createWuapiMcpHttpHandler();License
MIT
Available Tools
87 toolsadd_group_participantsAdd people to a groupA
Add participants to a group the account administers. Each result says whether it worked; someone who cannot be added directly comes back with an invite code.
Inputs: groupId (required), accountId (required), contactIds (required).
archive_chatArchive or unarchive a chatBIdempotent
Archive a chat on the linked devices, or unarchive it with archived: false.
Inputs: chatId (required), accountId (required), archived.
cancel_invitationCancel an invitationADestructiveIdempotent
Cancel an invitation: its link stops working. Completed invitations cannot be cancelled.
Inputs: confirm (required), invitationId (required).
cancel_messageCancel a queued messageADestructiveIdempotent
Stop a message that is still queued from being sent. It ends failed with error code cancelled. Fails with not_queued when the message already left; use delete_message for those.
Inputs: confirm (required), messageId (required).
check_numbersCheck numbers on WhatsAppARead-onlyIdempotent
Which of these phone numbers have WhatsApp, with their contact id, business name and username when known. Check before sending to a new number.
Inputs: phones (required), accountId (required).
create_accountConnect a new numberA
Start linking a new WhatsApp number. Pick proxyLocation with list_proxy_locations (near where the phone is). Then show the QR code with get_account_qr_code, or pass pairingPhone to link with an 8-character pairing code instead. A linked number is billable; a new organization links its first number with no card. To let someone else link their own number, use create_invitation.
Inputs: proxyLocation (required), name, projectId, historySync, +2 more.
create_groupCreate a groupA
Create a WhatsApp group with the account as owner, inside a community when communityId is given. Participants whose privacy settings refuse being added get an invite code instead (see the result).
Inputs: name (required), accountId (required), participants (required), communityId, +1 more.
create_incoming_webhookCreate an incoming webhookA
Create a secret URL that sends a WhatsApp message from one of the user's numbers to one fixed contact or group when something POSTs to it (JSON {text} or plain text, no API key). The URL is a credential, so it is not returned through this tool: the user copies it from the wuapi dashboard (Integrations, Incoming webhooks).
Inputs: to (required), name (required), accountId (required).
create_invitationInvite someone to link their numberA
Create a branded page (url) where a customer links their own WhatsApp number to your project by QR code or pairing code, without an account on wuapi. Send them the url: it is returned only here. With inviteeEmail, wuapi can email it too.
Inputs: methods, projectId, returnUrl, accountName, +8 more.
create_projectCreate a projectA
Create a project for one of your customers. Set externalId to your own customer id so you can address it as ext:<externalId>. Then invite the customer to link their number with create_invitation. Organization keys only.
Inputs: name (required), metadata, externalId, maxAccounts, +1 more.
create_webhookCreate a webhook endpointA
Register a public https URL to receive events as signed POST requests. The signing secret is not returned through this tool, to keep it out of the conversation: the user reveals it in the wuapi dashboard (Webhooks, rotate the endpoint's secret) and stores it with their server.
Inputs: url (required), events (required), projectId, idempotencyKey.
delete_chatDelete a chatADestructiveIdempotent
Delete a chat from the account's linked devices (the phone and WhatsApp Web). The messages wuapi stores are kept and still readable with list_messages. This cannot be undone on the devices.
Inputs: chatId (required), confirm (required), accountId (required), deleteMedia.
delete_incoming_webhookDelete an incoming webhookADestructiveIdempotent
Delete an incoming webhook. Its URL stops working at once, for whatever still calls it.
Inputs: confirm (required), incomingWebhookId (required).
delete_messageDelete a messageADestructiveIdempotent
Delete an outbound message for everyone (or only on the linked devices with forEveryone: false). A message still queued is cancelled instead and never sent. This cannot be undone.
Inputs: confirm (required), messageId (required), forEveryone.
delete_webhookDelete a webhook endpointADestructiveIdempotent
Delete a webhook endpoint. Its events stop at once, including deliveries still retrying.
Inputs: confirm (required), webhookEndpointId (required).
demote_group_participantsRemove admin rightsAIdempotent
Turn group admins back into regular members.
Inputs: groupId (required), accountId (required), contactIds (required).
edit_messageEdit a sent text messageAIdempotent
Change the text of an outbound text message, within WhatsApp's edit window (about 15 minutes). A message still queued is sent with the new text.
Inputs: text (required), messageId (required).
forward_messageForward a messageA
Forward a stored message (received or sent) to up to 5 chats of the same account, the way WhatsApp forwards: the recipients see it as forwarded and no file is uploaded again. Works for text, images, videos, audio, voice notes, documents, stickers, locations and contact cards; polls, calendar events, reactions, view-once and deleted messages answer not_forwardable, and so does a contact's story (only a story the account posted, which is a message, can be forwarded). A message that is forwardedManyTimes goes to one chat per call. Returns a list with one queued message per chat, in the order of to; check each with get_message. To label new content as forwarded, use a send tool with forwarded: true instead.
Inputs: to (required), messageId (required), idempotencyKey.
get_accountGet an accountARead-onlyIdempotent
One account: status, linked phone number, profile name, proxy location, disconnect reason and last error. hasQrCode says a QR code is waiting to be scanned (get it with get_account_qr_code).
Inputs: accountId (required).
get_account_qr_codeGet the QR code to link a numberARead-onlyIdempotent
The QR code to scan in WhatsApp (Settings > Linked devices > Link a device), as an image. Waits a few seconds for one when the account is still starting. QR codes rotate about every 20 seconds: call again if it expired. Returns the status instead when the account is already linked, and the pairing code when it links by phone number.
Inputs: accountId (required), waitSeconds.
get_chatGet a chatARead-onlyIdempotent
One conversation of an account: its name, its latest message in full, and WhatsApp's unread, pinned, archived and muted state (a field that is missing is not known yet, which is not the same as false). A chat wuapi stored no message of answers not_found.
Inputs: chatId (required), accountId (required).
get_contactGet a contactARead-onlyIdempotent
One contact of an account's address book, by number or lid: id: its saved name, profile name, username and picture id as wuapi stored them. A number that is not saved on the phone answers not_found; lookup_contacts asks WhatsApp about any number.
Inputs: accountId (required), contactId (required).
get_current_keyWho am IARead-onlyIdempotent
The organization, API key (name and last 4 characters, never the key) and project this server acts as. Call it first when you are unsure whether the key is an organization key or a project key.
Inputs: no named parameters.
get_groupGet a groupBRead-onlyIdempotent
A group's name, description, settings and participants with their roles (member, admin, owner).
Inputs: groupId (required), accountId (required).
get_group_invite_linkGet a group's invite linkARead-onlyIdempotent
The group's https://chat.whatsapp.com/... invite link. The account must be an admin.
Inputs: groupId (required), accountId (required).
get_incoming_webhookGet an incoming webhookARead-onlyIdempotent
One incoming webhook: its number, destination and whether it is enabled. Never its URL.
Inputs: incomingWebhookId (required).
get_invitationGet an invitationARead-onlyIdempotent
One invitation's status, and the linked accountId once completed.
Inputs: invitationId (required).
get_messageGet a messageARead-onlyIdempotent
One message with its status (queued, sent, delivered, read, failed, or received for inbound), content, and error when it failed. Received media is on demand by default (media.downloaded: false): pass fetchMedia: true to download it once and get a direct mediaFile.url that needs no API key.
Inputs: messageId (required), fetchMedia.
get_projectGet a projectARead-onlyIdempotent
One project: name, external id, status, account limit and count. Organization keys only.
Inputs: projectId (required).
get_usageGet this month's usageARead-onlyIdempotent
The current month's bill so far: billable numbers, proxy traffic, and the total in cents (USD). Organization keys only.
Inputs: no named parameters.
get_usage_by_projectGet usage per projectARead-onlyIdempotent
Numbers, proxy traffic and messages sent and received per project for a month, for rebilling your customers. With projectId, that project only. Organization keys only.
Inputs: month, projectId.
get_webhookGet a webhook endpointARead-onlyIdempotent
One webhook endpoint: its URL, events and whether it is active. Never its signing secret.
Inputs: webhookEndpointId (required).
leave_groupLeave a groupADestructiveIdempotent
Make the account leave a group. To come back it has to be added or invited again.
Inputs: confirm (required), groupId (required), accountId (required).
list_accountsList accountsARead-onlyIdempotent
List the linked WhatsApp numbers (accounts), newest first, with their status: ready can send; qr_ready waits for a QR scan; disconnected or failed need attention.
Inputs: limit, cursor, projectId.
list_chatsList chatsARead-onlyIdempotent
An account's conversations, the one with the newest message first: each chat's name, picture id, latest message and WhatsApp's unread, pinned, archived and muted state. Only chats wuapi stored a message of. A state field that is missing (unread, unreadCount, pinned, pinnedAt, archived, muted) is not known to wuapi yet, which is not the same as false. Filter by archived, unread or type, or search names, numbers and recent text with q. Read a conversation with list_messages and the chat's id as chatId.
Inputs: accountId (required), q, type, limit, +3 more.
list_contactsList contactsARead-onlyIdempotent
An account's address book as its phone synced it to wuapi, ordered by saved name: each contact's id, number, savedName, WhatsApp profile name, and the username and picture id seen so far. Only contacts the phone saved (or that have a business name); people the account just chatted with are in list_chats. Read from what wuapi stored, so about and deviceCount are not here: lookup_contacts asks WhatsApp for those. Search names, usernames and numbers with q.
Inputs: accountId (required), q, limit, cursor.
list_groupsList groupsARead-onlyIdempotent
Every group the account is in, read live from WhatsApp, with the participant count. A group inside a community has that community's id as communityId (absent when it is in none), and default: true when it is the community's announcement group. Use get_group for the participants.
Inputs: accountId (required), limit, cursor.
list_incoming_webhooksList incoming webhooksARead-onlyIdempotent
The incoming webhooks: secret URLs that send a WhatsApp message from one of your numbers to one fixed contact or group. Shows the name, the number, the destination and whether each is enabled. Never the URLs themselves.
Inputs: limit, cursor, projectId.
list_invitationsList invitationsBRead-onlyIdempotent
Invitations and their status: pending, in_progress, completed (with the new accountId), failed, cancelled or expired.
Inputs: limit, cursor, status, projectId.
list_messagesList messagesARead-onlyIdempotent
Messages wuapi stored, newest first: sent and received. Filter by account, chat and direction to read a conversation. Use list_chats to see which conversations an account has.
Inputs: limit, chatId, cursor, accountId, +2 more.
list_projectsList projectsBRead-onlyIdempotent
Projects: one per customer of a platform, each with its own numbers, keys, webhooks and usage. Organization keys only.
Inputs: limit, cursor, status, externalId.
list_proxy_locationsList proxy locationsARead-onlyIdempotent
Countries and cities a new number's proxy can exit from, for create_account and create_invitation. Search with q (sao finds Sao Paulo) or filter by country.
Inputs: q, limit, cursor, country.
list_webhooksList webhook endpointsARead-onlyIdempotent
The webhook endpoints that receive events: URL, events and whether active. Signing secrets are never shown here.
Inputs: limit, cursor, projectId.
lookup_contactsLook up contactsARead-onlyIdempotent
About text, picture id, WhatsApp username, business name and device count of 1 to 50 contacts, asked from WhatsApp (the saved names are in list_contacts). Usernames cannot be searched: WhatsApp does not let a linked device resolve an @username, so look contacts up by number or lid: id.
Inputs: accountId (required), contactIds (required).
lookup_whatsapp_infoLook up WhatsApp detailsARead-onlyIdempotent
Read details from WhatsApp through a linked number: a contact's profile picture or business profile, who a contact QR link or business message link points to, WhatsApp's AI bot directory, and the sticker pack or catalog order a message refers to. Nothing is changed.
Actions:
contact_picture: The contact's profile picture URL, when the account can see it.business_profile: A business contact's address, email, categories, time zone and opening hours.resolve_link: Who a contact QR link or business message link points to, and its prefilled text. Fails withnot_supportedwhen WhatsApp does not offer it to the account.bots: WhatsApp's AI bot directory as the account sees it. Fails withnot_supportedwhere WhatsApp does not offer it.sticker_pack: A sticker pack and its stickers.order: The products, quantities and totals of a catalog order received as a message.
Inputs: action (required), code, kind, limit, +7 more.
manage_block_listBlock or unblock contactsADestructiveIdempotent
The account's block list on WhatsApp: list blocked contacts, block a contact (it can no longer message or call the account) or unblock it.
Actions:
list: Blocked contacts, with their number andlid:id when known.block: Block a contact. Needsconfirm: true.unblock: Unblock a contact.
Inputs: action (required), limit, cursor, confirm, +2 more.
manage_brandingManage invitation brandingAIdempotent
How the invitation page and email look to your customers: display name, logo, accent color, support link, and whether the "Powered by wuapi" footer shows. Organization keys only.
Actions:
get: The current branding.update: Change any of the fields. Returns the branding.
Inputs: action (required), logoUrl, supportUrl, accentColor, +2 more.
manage_channelManage channelsA
WhatsApp channels (one-to-many broadcasts, ids end in @newsletter): list the ones the account follows or owns, read one and its posts, preview an invite, create a channel, follow, unfollow, mute and unmute, react to posts and count as a viewer. To post to a channel the account administers, use send_text or send_media with to set to the channel id.
Actions:
list: Channels the account follows or administers, with its role.get: One channel: name, description, subscriber count, the account's role and whether it is muted.preview_invite: The channel behind an invite code or link, without following it.list_messages: The channel's recent posts, newest first, with view and reaction counts.create: Create a channel with the account as owner. Returns it.follow: Follow the channel.unfollow: Stop following the channel.mute: Mute the channel's notifications.unmute: Unmute the channel.react: React to a post with an emoji, or remove the reaction with an empty string.mark_viewed: Count the account as a viewer of these posts.
Inputs: action (required), code, name, emoji, +9 more.
manage_communityManage communitiesA
WhatsApp communities: a community is a group with community: true that links other groups. Create one, list its groups and members, and link or unlink groups (the account must admin both). get_group reads the community itself; send to it like a group. To create a new group inside a community, use create_group with communityId; list_groups shows each group's communityId.
Actions:
create: Create a community with the account as owner. Returns it.list_groups: The groups linked to the community, marking its default (announcement) group.list_members: Members across the community's linked groups.link_group: Link a group into the community.unlink_group: Unlink a group from the community. The group itself stays.
Inputs: action (required), name, limit, cursor, +4 more.
manage_favorite_stickersManage favorite stickersAIdempotent
The account's favorite stickers: the star tab of WhatsApp's sticker picker, which WhatsApp keeps in sync with the phone. List them, get one's file, favorite a sticker from a message or an uploaded WebP file, or remove one. Adding and removing change the list on the phone too.
Actions:
list: The favorites, newest first, from what wuapi stored (no call to WhatsApp). Each hasmedia.urlandmedia.downloaded;animatedandemojisare null until its file was fetched.get_file: A direct URL to one favorite's file that needs no API key. Fetches the file from WhatsApp the first time, which needs the account ready.add: Favorite a sticker: passmessageIdoruploadId, one of them. A sticker that is a favorite already is returned as is.remove: Take a sticker out of the favorites. It can be favorited again.
Inputs: action (required), limit, cursor, uploadId, +4 more.
manage_group_joinsJoin groups and handle join requestsA
Joining groups: preview the group behind an invite link, join it, and, in a group that needs approval to join, list, approve or reject the pending requests (the account must be an admin).
Actions:
preview_invite: The group behind an invite code or link, without joining: name, description, size.join: Join a group with an invite code or link. Returns the group id; a group that needs approval makes it a pending request instead.list_requests: Pending requests to join the group.approve_requests: Let these people in. Each result says whether it worked.reject_requests: Turn these requests down. Each result says whether it worked.
Inputs: action (required), code, limit, cursor, +4 more.
manage_group_settingsChange a group's info and settingsADestructiveIdempotent
Change a group the account administers: its name, description and settings (who can send, who can edit the info, whether joining needs approval, who can add members), and its picture.
Actions:
update: Change any of name, description, announce, locked, joinApproval and memberAddMode. Returns the group.set_picture: Set the group picture from an https URL or base64 JPEG. Returns the picture.delete_picture: Remove the group picture. Needsconfirm: true.
Inputs: action (required), name, locked, confirm, +8 more.
manage_labelsManage labelsADestructiveIdempotent
WhatsApp Business labels on chats and messages: create or edit a label, delete it, and add it to or remove it from a chat or a message. WhatsApp Business accounts only; others answer not_supported. There is no endpoint that lists labels: they arrive in label.updated webhook events, and upsert creates one under an id you choose.
Actions:
upsert: Create the label with this id, or rename or recolor it. Returns the label.delete: Delete the label. Needsconfirm: true.label_chat: Add the label to a chat.unlabel_chat: Remove the label from a chat.label_message: Add the label to a message, as the account that sent or received it.unlabel_message: Remove the label from a message.
Inputs: action (required), name, color, chatId, +4 more.
manage_privacyManage privacy settingsAIdempotent
The account's WhatsApp privacy settings: who sees its last seen, profile picture, stories and online status, who may add it to groups or call it, and whether it sends read receipts.
Actions:
get: Every privacy setting.get_story_privacy: Who sees the account's stories: its contacts, all but some, or only some.update: Change one or more settings. Each setting is one change on WhatsApp. Returns every setting.
Inputs: action (required), online, callAdd, profile, +6 more.
manage_profileManage the account's profileADestructive
The linked number's own WhatsApp profile: its display name and About text, its profile picture, and its contact QR link (a https://wa.me/qr/... link that opens a chat with it).
Actions:
update: Change the display name, the About text, or both.set_picture: Set the profile picture from an https URL or base64 JPEG. Returns the picture.delete_picture: Remove the profile picture. Needsconfirm: true.get_contact_link: The account's contact QR link.reset_contact_link: Revoke the contact QR link and return a new one. The old link stops working. Needsconfirm: true.
Inputs: action (required), name, about, confirm, +3 more.
manage_projectUpdate, delete or audit a projectADestructiveIdempotent
Change a project (rename it, set its external id, metadata or account limit, suspend or resume it), delete it, and list or revoke its API keys. Suspending refuses every send and write in the project while reads and inbound messages keep working. Organization keys only. New project keys are created in the wuapi dashboard: no tool returns an API key.
Actions:
update: Change any of name, externalId, metadata, maxAccounts and status. Returns the project.delete: Delete the project: its keys stop working at once, and its numbers are logged out and deleted in the background with its webhook endpoints. Needsconfirm: true.list_keys: The project's API keys, revoked ones included: name, last 4 characters, last use. Never the key itself.revoke_key: Revoke one of the project's API keys. It stops working at once. Needsconfirm: true.
Inputs: action (required), name, limit, cursor, +7 more.
manage_storiesRead and answer storiesADestructive
Stories (WhatsApp Status): read the ones the account's contacts posted in the last 24 hours, mark one as viewed, react or reply to it, and see or delete the ones the account posted (post with post_story). Reading stories tells the contact nothing; view does, so call it only when the user wants the contact to know. Contacts' stories arrive only for accounts with stories turned on.
Actions:
list: Contacts' stories, grouped by contact, the contact with the newest story first.list_own: The stories the account posted in the last 24 hours, with how many contacts saw each.get: One story. WithfetchMedia, also its file's direct URL.viewers: Who saw a story the account posted, with their reactions, the latest viewer first.view: Tell the contact the account saw their story: the account then shows in the story's viewers. Only when the user asked for it.authorNotified: falsein the answer means WhatsApp did not tell them (read receipts are off).react: React to a contact's story with an emoji, or remove the reaction with an empty string. Only the story's author sees it.reply: Reply to a contact's story with text: a message in the chat with its author, who sees it as a reply to their story.delete: Delete a story the account posted, for everyone. Needsconfirm: true.
Inputs: action (required), text, emoji, limit, +8 more.
mark_chat_readMark a chat read or unreadAIdempotent
Clear (or set, with unread: true) the chat's unread badge on the linked devices. Sends no read receipts; use send_read_receipts for blue ticks.
Inputs: chatId (required), accountId (required), unread.
mute_chatMute or unmute a chatAIdempotent
Mute a chat's notifications for a while or until unmuted, or unmute it with muted: false.
Inputs: chatId (required), accountId (required), muted, durationSeconds.
pin_chatPin or unpin a chatAIdempotent
Pin a chat to the top on the linked devices, or unpin it with pinned: false.
Inputs: chatId (required), accountId (required), pinned.
post_storyPost a storyA
Post a story from the account: text on a colored background, or an image or video with an optional caption, from a public URL (mediaUrl) or a file uploaded with upload_file (uploadId).
Inputs: accountId (required), text, type, mediaUrl, +3 more.
promote_group_participantsMake participants adminsBIdempotent
Make participants admins of a group the account administers.
Inputs: groupId (required), accountId (required), contactIds (required).
react_to_messageReact to a messageAIdempotent
React to a message with an emoji, as the account that received or sent it. An empty string removes the reaction.
Inputs: emoji (required), messageId (required).
reconnect_accountReconnect an accountAIdempotent
Restart the account's session. Use it when an account is disconnected; when the link is no longer valid it produces a fresh QR code.
Inputs: accountId (required).
reject_callReject an incoming callA
Reject a WhatsApp call ringing on the account, with the id and from of its call.received webhook event. To reject every call automatically, set rejectCalls with update_account.
Inputs: from (required), callId (required), accountId (required), idempotencyKey.
remove_group_participantsRemove people from a groupADestructiveIdempotent
Remove participants from a group the account administers.
Inputs: confirm (required), groupId (required), accountId (required), contactIds (required).
reply_to_messageReply to a messageA
Reply with text to a message, quoting it, in the same chat and from the same account. Works for messages in direct chats and groups.
Inputs: text (required), messageId (required), idempotencyKey.
request_pairing_codeGet a pairing codeA
Link by phone number instead of QR code: returns an 8-character code the owner types in WhatsApp. It lives about 160 seconds. Fails with already_linked when the account is ready.
Inputs: phone (required), accountId (required).
resend_invitationResend an invitationADestructive
Give an invitation a new link and expiry and email it again when it has an inviteeEmail. The previous url stops working; send the new one from the result. An account the invitee already started is kept, so they resume where they left off.
Inputs: confirm (required), invitationId (required), idempotencyKey.
reset_group_invite_linkReset a group's invite linkADestructive
Revoke the group's invite link and return a new one. Anyone holding the old link can no longer join with it.
Inputs: confirm (required), groupId (required), accountId (required).
save_contactsSave contacts on the phoneAIdempotent
Save contacts on the linked phone, in WhatsApp and in the phone's own address book, as if they were saved on the phone. Any number can be saved. Saves are queued (also while the account reconnects): each contact's phoneSync says pending, then synced or failed (not_on_whatsapp, account_offline), and contact.updated fires; the phone shows the name about a minute later. Removing a contact is not supported. backfill_auto_save queues the contacts of the account's existing direct chats, named with its auto-save templates (update_account contactAutoSave), at most 200 a day.
Actions:
one: Save one contact undersavedName. Returns the contact with itsphoneSync.many: Save up to 500 contacts at once. Returns the batch id and each contact's status.backfill_auto_save: Queue the auto-save of the contacts of the account's existing direct chats.
Inputs: action (required), contacts, accountId, contactId, +2 more.
send_buttonsSend buttonsA
Send a message with 1 to 10 tappable buttons. A quick reply (id) answers as a message.received of type button with button.id; a link (url, HTTPS) opens when tapped. WhatsApp disables a quick reply in the sender's own view of the message; recipients can tap it. The body is text, with an optional footer.
Inputs: to (required), text (required), buttons (required), accountId (required), +4 more.
send_contactSend contact cardsC
Send one contact card, or up to 20 in one message.
Inputs: to (required), contacts (required), accountId (required), mentions, +2 more.
send_locationSend a locationC
Send a map pin, with an optional place name and address.
Inputs: to (required), latitude (required), accountId (required), longitude (required), +5 more.
send_mediaSend an image, video, audio or documentA
Send a file: an image, video, audio, voice note (ogg/opus), document or sticker, with an optional caption. Give exactly one of url (a public URL wuapi downloads itself) or uploadId (a file uploaded before with upload_file). Images go out at the account's image quality (standard by default, as WhatsApp sends a photo); pass quality to change it for one image, or send the file as a document to deliver it untouched.
Inputs: to (required), type (required), accountId (required), url, +9 more.
send_pollSend a pollA
Send a poll with 2 to 12 options. Votes arrive as poll.voted webhooks and in the message's poll tally (get_message).
Inputs: to (required), options (required), question (required), accountId (required), +4 more.
send_read_receiptsSend read receiptsAIdempotent
Send read receipts (blue ticks) for inbound messages in a chat: the given ids, or every unread inbound message wuapi stores for it.
Inputs: chatId (required), accountId (required), messageIds.
send_textSend a text messageA
Send a text message from a linked number to a contact, group or channel. The message is queued and sent at the account's pace; the result has its id and queued status. Send only to people who expect it.
Inputs: to (required), text (required), accountId (required), mentions, +2 more.
set_disappearing_timerSet disappearing messagesAIdempotent
Turn disappearing messages on or off: for one chat with chatId, or without it the account's default timer, which new chats start with. Messages already stored in wuapi are kept.
Inputs: accountId (required), durationSeconds (required), chatId.
set_presenceSet presence or typingAIdempotent
Presence as contacts see it: the account online or offline, typing or recording in a chat, and presence events from a contact. Typing shows until paused or the next message. With typing turned on in the account's pacing (update_account), sends show it on their own.
Actions:
online: Show the account as online to contacts.offline: Show the account as offline.typing: Show typing in a chat.recording: Show recording audio in a chat.paused: Clear the typing or recording indicator in a chat.subscribe: Receivecontact.presence_updatedwebhook events when the contact comes online, goes offline or types. Fails withnot_supportedwhen the account cannot receive presence events.
Inputs: action (required), chatId, accountId, contactId.
star_messageStar or unstar a messageAIdempotent
Star a message on the phone and the other linked devices, or unstar it with starred: false. Returns the message.
Inputs: messageId (required), starred.
unlink_accountLog out or delete an accountADestructiveIdempotent
Unlink a WhatsApp number from wuapi. Both actions log the number out on WhatsApp and stop billing for it. Use logout to keep the account and its stored messages (reconnect_account then shows a new QR code to link again); use delete to remove the account for good.
Actions:
logout: Unlink the phone and stop billing, keeping the account and its messages. Returns the account. Needsconfirm: true.delete: Log out, delete the account and stop billing for it. Cannot be undone; fires no event. Needsconfirm: true.
Inputs: action (required), confirm, accountId.
update_accountUpdate an accountAIdempotent
Change an account's settings: rename it, reject incoming calls automatically, turn on and tune its pacing (the anti-ban protections, off by default), choose whether the next link imports recent chats, choose which received media is downloaded right away (the rest on demand), choose the quality its images are sent at, save the contacts of new chats on the phone automatically (contactAutoSave), or move its proxy location. Pacing applies from the next send. A proxy location change gives the number a new exit IP and reconnects it; it is refused within 10 minutes of the previous change. Returns the updated account with its pacing.
Inputs: accountId (required), name, pacing, historySync, +6 more.
update_incoming_webhookUpdate an incoming webhookAIdempotent
Rename an incoming webhook, or pause it (enabled: false) and resume it. The destination cannot change: create another one.
Inputs: incomingWebhookId (required), name, enabled.
update_webhookUpdate a webhook endpointAIdempotent
Change a webhook endpoint's URL or events, or pause it (active: false) and resume it.
Inputs: webhookEndpointId (required), url, active, events.
upload_fileUpload a file to sendA
Upload a file whose bytes you have (not a URL) so it can be sent: returns an upload whose id goes in uploadId of send_media or post_story. The file can be sent for 24 hours, to any number of chats. Up to 5 MB; the hosted server takes requests up to 1 MB, so about 700 KB of file there. For a file that is already at a public URL, skip this and pass the URL to send_media.
Inputs: base64 (required), mimeType (required), filename, idempotencyKey.
vote_in_pollVote in a pollAIdempotent
Vote in a poll message as the account that received or sent it, with the names of its options (get_message shows them). An empty list retracts the vote. Returns the poll with its tally.
Inputs: options (required), messageId (required), idempotencyKey.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
TDQS
Scored across 87 tools
The tools mostly target clearly distinct resources and actions, and the rich descriptions clarify overlaps like list_messages vs manage_channel.list_messages or get_contact vs lookup_contacts. A few umbrella tools (manage_labels, manage_channel, manage_stories) and similarly named contact/chat tools could still require careful reading, but genuine misselection is limited.
Names consistently use snake_case and mostly follow a verb_noun pattern (send_text, get_chat, create_group, manage_channel). The repeated manage_* convention for multi-action resources is predictable, and there are no style collisions such as camelCase or vague single-word tools.
Eighty-seven tools is very large even for a broad WhatsApp API surface, and many tools bundle multiple actions internally, making the external surface unusually heavy. The domain is extensive, but the set would benefit from consolidation or clearer grouping.
Coverage is exceptionally broad: accounts, messaging, media, chats, contacts, groups, communities, channels, stories, labels, privacy, webhooks, projects, invitations, billing, and more. The tool surface handles lifecycle operations and edge cases with few obvious dead ends.
Maintenance
Related MCP Connectors
WhatsMCP connects Claude and other MCP-compatible AI agents directly to WhatsApp. Send and receive text, images, documents, and voice notes; manage groups (create, add/remove members, promote admins); look up contacts and profiles; follow channels; and read call and message history — all through a standard MCP interface. For voice use cases, WhatsMCP offers SIP-based calling plans (inbound-only, or full inbound/outbound) so AI voice agents can answer and place WhatsApp calls, plus low-latency WebSocket integrations with voice agent providers like ElevenLabs. Multiple WhatsApp accounts can be paired and managed per workspace, with webhook support for real-time inbound message delivery to your own infrastructure.
Drive WhatsApp from any MCP client: pair devices, send text and media, manage contacts and groups.
WhatsApp (Web + Business API), SMS, contacts, and call records via 2Chat's MCP server.
Let Claude or ChatGPT search, read and send your WhatsApp messages over MCP. OAuth sign-in.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables sending messages, managing templates, uploading media, and configuring webhooks for WhatsApp Business via the MCP protocol.10 npm5MIT
- AlicenseNot gradedqualityCmaintenanceEnables programmatic WhatsApp automation through MCP, including sending messages, managing chats and contacts, searching conversation history, and exchanging media files.MIT
- FlicenseNot gradedqualityCmaintenanceEnables linking a WhatsApp number via QR code and then sending, reading, listing chats, and checking status through MCP tools, with per-account API key authentication.-
- AlicenseNot gradedqualityCmaintenanceEnables agents and MCP clients to read and send WhatsApp messages and media, search chats, and manage contacts and groups after a one-time QR pairing.37 npmMIT