AI

WhatsApp API for AI agents: connecting Claude, n8n and LangChain

An AI agent that can only talk to you in a chat window is half useful. The other half is reaching the people who already live in WhatsApp: customers, leads, your own team. This guide covers three ways to wire a WhatsApp number into an agent (an MCP server, n8n and Python tools) and the locks you want in place before the agent is allowed to send anything. Disclosure: ZapZap API is an unofficial API. You connect a normal number by QR code, with no Meta business verification and no message templates. That is also its main risk, so a whole section is about it.

Published on · 6 min read

This article is also available in: Português · Español

Pick the route by where the agent runs

Where the agent runsRouteNeeds code?
---------
A desktop assistant (Claude Desktop, Claude Code, Cursor)MCP serverNo, one config entry
Triggered automations (a customer writes, the agent answers)n8n flowLittle
Your own product or backendPython or Node tools (LangChain, CrewAI, your own loop)Yes

All three talk to the same REST API: https://api.zapzapapi.com/api/v1/{instanceId}/..., authenticated with two headers, x-api-key and x-api-secret (authentication). If your agent framework reads OpenAPI, the spec is public: ZapZap OpenAPI. The AI agents page collects the three options in one place.

Route 1: an MCP server for Claude and Cursor

MCP (Model Context Protocol) lets an AI client discover tools from a server and call them during a conversation. zapzap-mcp exposes 25 curated tools (send text and media, polls, buttons, check numbers, find messages, groups, webhooks) instead of 150-plus raw endpoints, so the model has fewer wrong choices.

The code is open source at github.com/zapzapapi/zapzap-mcp and is not on npm yet. So you install it from the repository: clone, install and build (Node 18 or newer), then point your client at the compiled file.

Two behaviors worth knowing. Send tools are annotated as non-idempotent and destructive, so MCP clients ask you to confirm each send. And ZAPZAP_READ_ONLY=true removes every write tool, which is the right default for the first week (it is already on in the examples below).

Try prompts like: check that my instance is connected, then tell me which of these three numbers have WhatsApp. Approve each call by hand until you trust it. If you would rather write your own server, the MCP server walkthrough has the TypeScript code.

Instalar a partir do GitHub

git clone https://github.com/zapzapapi/zapzap-mcp
cd zapzap-mcp
npm install
npm run build
# Claude Code:
claude mcp add zapzap -e ZAPZAP_API_KEY=your-api-key -e ZAPZAP_API_SECRET=your-api-secret -e ZAPZAP_READ_ONLY=true -- node "$(pwd)/dist/src/index.js"

Claude Desktop ou Cursor (JSON)

{
  "mcpServers": {
    "zapzap": {
      "command": "node",
      "args": ["/path/to/zapzap-mcp/dist/src/index.js"],
      "env": {
        "ZAPZAP_API_KEY": "your-api-key",
        "ZAPZAP_API_SECRET": "your-api-secret",
        "ZAPZAP_READ_ONLY": "true"
      }
    }
  }
}

Route 2: an n8n flow with an AI Agent node

The flow has five nodes. 1) Webhook receives the messages event from your instance (register the production URL with the webhook endpoint). 2) Code extracts number, text and message id, and drops events where fromMe is true; skip this and the agent replies to its own replies in a loop. 3) AI Agent with window memory keyed by the contact's number, so each customer has a separate conversation; put the handoff rule in the system prompt, for example answer with one fixed sentence when the customer asks for a person. 4) IF checks for that fixed sentence and marks the contact as with a human. 5) HTTP Request calls POST /api/v1/{instanceId}/send/text with number and text.

There is also a community node, n8n-nodes-zapzap (version 0.1.3 on npm). Unverified community nodes only run on self-hosted n8n, so on n8n Cloud use the HTTP Request node with the same headers. The full walkthrough is in AI agent on WhatsApp with n8n, and there are ready workflows in the n8n templates.

Route 3: tools for LangChain (Python)

A tool is a function with a name, a description and typed arguments. The model decides when to call it; the function decides what is actually allowed. Keep the locks in the function, never in the prompt. The example below has two tools: check numbers, and send text only to an allowed list, with sending off until you set ZAPZAP_ENABLE_SEND=true.

Wire the tools to an agent with LangChain 1.x (create_agent) or CrewAI's tool decorator, and check your installed version's docs, because both projects change fast. Human approval before real sends comes from your framework's interrupt or human-input features. The full version is in the LangChain and CrewAI guide.

Python: ferramentas

import os
import re
import requests
from langchain_core.tools import tool

BASE = "https://api.zapzapapi.com/api/v1"
INSTANCE = os.environ["ZAPZAP_INSTANCE_ID"]
HEADERS = {
    "x-api-key": os.environ["ZAPZAP_API_KEY"],
    "x-api-secret": os.environ["ZAPZAP_API_SECRET"],
}
ALLOWED = {n.strip() for n in os.environ.get("ZAPZAP_ALLOWED", "").split(",") if n.strip()}
SEND_ENABLED = os.environ.get("ZAPZAP_ENABLE_SEND") == "true"


def _post(path: str, body: dict) -> dict:
    r = requests.post(f"{BASE}/{INSTANCE}{path}", json=body, headers=HEADERS, timeout=20)
    r.raise_for_status()
    return r.json()


@tool
def check_whatsapp_numbers(numbers: list[str]) -> str:
    """Check which phone numbers are registered on WhatsApp.
    Numbers: country code plus digits only, for example 5511999999999."""
    clean = [n for n in numbers if re.fullmatch(r"\d{10,15}", n)]
    if not clean:
        return "No valid numbers. Use country code and digits only."
    return str(_post("/chat/check", {"numbers": clean}))


@tool
def send_whatsapp_text(number: str, text: str) -> str:
    """Send a WhatsApp text to ONE allowed recipient. Never invent numbers."""
    if not re.fullmatch(r"\d{10,15}", number):
        return "Invalid number format."
    if number not in ALLOWED:
        return "Blocked: this number is not on the allowed list."
    if len(text) > 1000:
        return "Blocked: text too long."
    if not SEND_ENABLED:
        return f"Safe mode, not sent. Would send to {number}: {text[:80]}"
    return str(_post("/send/text", {"number": number, "text": text}))

Python: agente

from langchain.agents import create_agent

agent = create_agent(
    model="provider:model-name",   # fill in the provider you have a key for
    tools=[check_whatsapp_numbers, send_whatsapp_text],
    system_prompt="You help the support team. Only message people on the allowed list.",
)
result = agent.invoke({"messages": [{"role": "user", "content": "Is 5511999999999 on WhatsApp?"}]})

The locks, whatever the route

LockWhy
------
Allowed list of recipients, inside the toolThe model can be wrong or be talked into something
Human approval per sendLast barrier before a real message
Read-only mode firstLets you test lookups without any send risk
Hourly cap and loggingContains a looping agent and gives you an audit trail
A dedicated instanceKeeps risk away from your main number
Treat incoming text as untrustedA customer can write instructions aimed at the model (prompt injection)

The risk you should not skip

An unofficial API goes against WhatsApp's terms and a number can be limited or banned. An agent does not reduce that risk; it can raise it if it starts cold conversations at volume. Anti-block features such as automatic typing delay and pacing reduce the risk, they do not remove it, and nobody honest can promise otherwise.

Use agents to answer people who wrote first, to support your team and for internal tasks, and only message people who agreed to hear from you. See the anti-ban guide and the acceptable use policy. If you need an officially sanctioned channel, use Meta's Cloud API: the official vs unofficial comparison lays out when each one wins.

Try it

R$15 per active number per month, prepaid, with no per-message fee (pricing). New accounts get a welcome credit when the phone is verified, subject to the current campaign. The quick start takes you from account to first message.

Quick summary

  • MCP for desktop assistants, n8n for event-driven automation, Python tools for your backend: all three use the same REST API.
  • zapzap-mcp is open source on GitHub and not on npm yet: install it by cloning and building the repository.
  • Keep the locks inside the tool: allowed recipients, human approval, read-only mode and an hourly cap.
  • An AI agent does not lower the ban risk of the number. Use it to answer people who wrote first and for internal tasks.

Also read

Frequently asked questions

Is the ZapZap MCP server on npm yet?

Not yet. The code is open source at github.com/zapzapapi/zapzap-mcp: clone it, run npm install and npm run build, and point your MCP client at the compiled file. This guide will be updated when it is published to npm.

Can I let the agent send messages on its own?

You can, but start with human approval per send, an allowed list of recipients and read-only mode. Keep the locks inside the tool, not only in the prompt, because the model can be wrong or be led astray by incoming text.

Does n8n Cloud accept the ZapZap node?

Unverified community nodes only run on self-hosted n8n. On n8n Cloud use the HTTP Request node with the x-api-key and x-api-secret headers.

Does using an AI agent raise the ban risk of the number?

It can, if the agent starts cold conversations at volume. An unofficial API goes against WhatsApp's terms and no feature removes the risk. For a sanctioned channel, use Meta's official API.

Give your agent a WhatsApp channel

Connect a test number, start in read-only mode and turn on sending once the locks are in place. R$15 per active number per month.