# Connect: Claude and other AI apps (MCP)

> Docs index: https://wahasend.com/llms.txt · Everything in one file: https://wahasend.com/docs.md · API base URL: https://app.wahasend.com/api/v1

With **Connect**, an AI app works your WhatsApp number for you: it reads your chats, drafts and sends replies, prospects, sends and opens files. It speaks MCP (Model Context Protocol) over HTTP. Server address:

```text
https://app.wahasend.com/api/v1/mcp
```

The AI does the thinking and the writing (in its own account); WahaSend only moves messages and applies the [sending limits](https://wahasend.com/docs/connect.md#connect-limits).

<a id="connect-claude"></a>

### 1. claude.ai, Claude Desktop and the Claude phone app (sign-in, no key)

1. In Claude, open **Settings → Connectors** and tap **Add custom connector**.
2. Name: `WahaSend`. Link: `https://app.wahasend.com/api/v1/mcp`.
3. Tap **Connect**, sign in to WahaSend, choose the WhatsApp numbers Claude may use (one, several, or **All my numbers**, which includes numbers you add later) and tap **Allow**.

Behind it is standard OAuth 2.1 (authorization code + PKCE, dynamic client registration, scope `whatsapp`). Access tokens last 60 minutes and renew themselves for 30 days. The connection reaches only the numbers you chose, and works only on `/mcp`. Only owners and admins can allow it. On the **Connect** page you see connected apps with their numbers, change their numbers without reconnecting (it applies at the next request), or disconnect them; a password change or leaving the organization also ends them. A deleted number leaves the apps that had it; an app left with no number is disconnected.

<a id="connect-key"></a>

### 2. Claude Code, Cursor, Codex (with your key)

Replace `YOUR_KEY` with your account key (dashboard → **Connect**).

Claude Code:

```bash
claude mcp add --transport http wahasend https://app.wahasend.com/api/v1/mcp --header "Authorization: Bearer YOUR_KEY"
```

Claude Desktop or Cursor (MCP settings):

```json
{
  "mcpServers": {
    "wahasend": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://app.wahasend.com/api/v1/mcp",
        "--header",
        "Authorization: Bearer YOUR_KEY"
      ]
    }
  }
}
```

Codex: ~/.codex/config.toml:

```toml
[mcp_servers.wahasend]
url = "https://app.wahasend.com/api/v1/mcp"
bearer_token_env_var = "WAHASEND_API_KEY"
```

…then, in the terminal where you start Codex:

```bash
export WAHASEND_API_KEY=YOUR_KEY
```

Codex can also sign in without a key: `codex mcp add wahasend --url https://app.wahasend.com/api/v1/mcp` opens your browser; `codex mcp login wahasend` signs in again later. Any other MCP client: give it the link and send the key as `Authorization: Bearer` (or `X-API-Key`).

<a id="connect-chatgpt"></a>

### 3. ChatGPT

1. Use ChatGPT on the web with a paid plan (Plus, Pro, Business, Enterprise or Education); the phone app can't add apps.
2. In **Settings**, turn on **Developer mode**.
3. Create an app named `WahaSend` with the link `https://app.wahasend.com/api/v1/mcp` and Authentication **OAuth**.
4. Sign in to WahaSend, choose the numbers it may use and tap **Allow**. In a chat, open **+** → Developer mode → WahaSend.

<a id="connect-numbers"></a>

### Several WhatsApp numbers

One connection can work several of your numbers. Which ones: a Claude sign-in reaches the numbers chosen when you allowed it (or all of them); the account key reaches every number of the organization; a per-number key only its own. No tool ever sees or uses another number.

- `list_numbers` gives each number its id, phone and name (label), whether it is connected, whether WhatsApp may have blocked it, whether it is the default, and what it may still send today.
- Chats, replies and messages say which number they are on (`number`: id, phone, label). A contact who wrote to two numbers has two conversations. `list_chats`, `read_chat`, `new_replies` and `find_contact` take an optional `number` (its id, phone or label) to look at one number only.
- `send_message`, `send_attachment` and `send_to_many` take an optional `from` (id, phone or label). Without it, a reply goes out from the number the contact last talked with, and a first message from your default number. When several numbers could send and none is the default, the tool answers with the candidates and the AI asks you. The result says which number sent (`number`) and why (`numberChosenBy`).
- Limits stay per number: each number has its own spacing, hourly and daily caps and new-contact limit, with your organization's settings. A message is never moved to another number because one is limited, disconnected or may be blocked ([send groups](https://wahasend.com/docs/phone-numbers.md#several-numbers) don't apply to Connect). "New contact" means new to the sending number.
- `check_numbers` and `daily_allowance` take an optional `number`; `daily_allowance` without one lists every number. `open_attachment` fetches the file through the number its message is on.

<a id="connect-tools"></a>

### Tools

The server gives the AI these tools and a set of rules (ask before a first message, reply in the contact's language, never work around a limit…). Tool answers are JSON; a refused send says why and when it lifts. There is also a `prospecting` prompt (arguments `offer`, `language`).

Descriptions below are what the AI reads, in English, with the recommended limits.

#### list_numbers

List my WhatsApp numbers. The user's WhatsApp numbers this connection may use: name (label), phone, whether it is connected, whether WhatsApp may have blocked it, whether it is the default, and what it may still send today. Every other tool names numbers the same way (id, phone or label).

No input.

#### send_message

Send a WhatsApp message. Send one text message from one of the user's WhatsApp numbers. From: pass `from` (a number's id, phone or label, see list_numbers) to choose which of the user's numbers sends; without it, a reply goes out from the number this contact last talked with, a first message from the default number — when that is unclear the result lists the numbers: ask the user. It never switches to another number by itself. Messages are spaced 30–90 s apart, capped at 60 an hour, 200 a day; the result says when it will go out. A first message to a new contact needs confirmed=true (after the user approved it) and counts against a small daily limit of that number.

- `to` string, required (1–100 chars): A phone number (+34612345678), a WhatsApp id exactly as returned (…@lid), or a contact name ("Percy")
- `text` string, required (1–4096 chars)
- `from` string, optional (1–100 chars): Which of the user's numbers sends (id, phone or label, see list_numbers). Omit to reply from the number the contact last talked with (or the default number for someone new).
- `confirmed` boolean, optional: Required for a first message to a new contact: true only after the user approved this recipient, number and text

#### send_attachment

Send a file on WhatsApp. Send one file (photo, PDF or other document, voice note, video) from one of the user's WhatsApp numbers, with an optional caption. Give exactly one source: forward_from (a message with a file in that number's chats, by the ids read_chat / new_replies return — WhatsApp forwards only within one number), url (a public https link to the file) or content_base64 + filename + mimetype (a file you made or the user gave you). The kind is chosen from the file: JPEG/PNG/WebP → photo, ogg/opus → voice note, MP4 → video, anything else allowed → document. Programs, scripts, web pages and archives are refused. From: pass `from` (a number's id, phone or label, see list_numbers) to choose which of the user's numbers sends; without it, a reply goes out from the number this contact last talked with, a first message from the default number — when that is unclear the result lists the numbers: ask the user. It never switches to another number by itself. Same rules as send_message: Messages are spaced 30–90 s apart, capped at 60 an hour, 200 a day; the result says when it will go out.

- `to` string, required (1–100 chars): A phone number (+34612345678), a WhatsApp id exactly as returned (…@lid), or a contact name ("Percy")
- `caption` string, optional (0–1024 chars): Text shown with the file (not for voice notes)
- `from` string, optional (1–100 chars): Which of the user's numbers sends (id, phone or label, see list_numbers). Omit to reply from the number the contact last talked with (or the default number for someone new).
- `confirmed` boolean, optional: true only after the user approved this recipient, number, file and caption. Required for a first message to a new contact, and to forward a file someone sent to another person
- `forward_from` object, optional
- `forward_from.chat` string, required (1–100 chars): The chat the file is in: phone, WhatsApp id (…@lid) or contact name
- `forward_from.message_id` uuid, required: The `id` of the message with the file, as read_chat / new_replies returned it
- `url` string (URL), optional (0–2048 chars): A public https link to the file itself
- `content_base64` string, optional (4–6991531 chars): The file's bytes, base64 (at most 5 MB)
- `filename` string, optional (1–200 chars): With content_base64: the file name, e.g. "price-list.pdf"
- `mimetype` string, optional (3–128 chars): With content_base64: its type, e.g. "application/pdf", "text/csv"

#### send_to_many

Send a message to several contacts. Send a text to a list of contacts, all from one of the user's numbers (from; the default number when omitted — never split across numbers). They go out one by one, 30–90 s apart; the result lists when each is scheduled and which could not be sent because a limit of that number was reached. {{name}} in the text is replaced per recipient. Requires confirmed=true, which you may only set after showing the user the recipients, the number and the text and getting their OK.

- `recipients` object[], required (1–50 items)
- `recipients[].phone` string, required (7–64 chars): Phone number in international format (+34612345678), or the WhatsApp id exactly as list_chats / new_replies returned it (e.g. 12345678901234@lid). Never strip "@lid".
- `recipients[].name` string, optional (0–100 chars)
- `text` string, required (1–4096 chars): May contain {{name}}
- `from` string, optional (1–100 chars): Which of the user's numbers sends (id, phone or label, see list_numbers). Omit to reply from the number the contact last talked with (or the default number for someone new).
- `confirmed` boolean, optional: Required: true only after the user approved this exact list, number and text

#### check_numbers

Check which numbers are on WhatsApp. Tell valid WhatsApp numbers from invalid ones before messaging. Does not send anything. Checked through one of the user's numbers (`number`; the default one when omitted).

- `phones` string[], required (1–500 items)
- `number` string, optional (1–100 chars): The user's number to check through (id, phone or label); people who already talked to it need no check

#### find_contact

Find a contact. Look someone up in the contacts of the user's numbers by name or part of their number. Returns their phone (and WhatsApp id when known) and which of the user's numbers know them. Use it before messaging someone the user names.

- `query` string, required (1–100 chars)
- `limit` integer, optional (1–50)
- `number` string, optional (1–100 chars): Only the contacts of this one of the user's numbers (id, phone or label)

#### list_chats

List conversations. Conversations of the user's numbers, newest first, with unread counts — one per contact and number (`number` says which of the user's numbers it is on). `phone` is either digits or a WhatsApp id ending in @lid; use it as-is to reply, from the same number.

- `limit` integer, optional (1–200)
- `search` string, optional (0–100 chars): Filter by phone or name
- `number` string, optional (1–100 chars): Only the conversations of this one of the user's numbers (id, phone or label)

#### read_chat

Read a conversation. The exchange with one contact, oldest first: what was sent (with delivery status) and what they replied. Each message has an `id` and the `number` of the user's it is on; messages with a file carry an `attachment` label — pass the id to open_attachment to see it. When the contact talked with several of the user's numbers, pass `number` to read one conversation; never mix them.

- `phone` string, required (7–64 chars): Phone number in international format (+34612345678), or the WhatsApp id exactly as list_chats / new_replies returned it (e.g. 12345678901234@lid). Never strip "@lid".
- `limit` integer, optional (1–500)
- `number` string, optional (1–100 chars): The one of the user's numbers whose conversation to read (id, phone or label)

#### open_attachment

Open an attachment. Open the file attached to one WhatsApp message: a photo is shown to you, a PDF or Word (.docx) file comes back as text, a voice note as a transcript. Videos and other files: details only. Use only when the file matters for what the user asked. The file is fetched through the number the message is on.

- `chat` string, required (1–100 chars): The contact: phone number, WhatsApp id exactly as returned (…@lid), or a contact name — the same chat you read with read_chat
- `message_id` uuid, required: The `id` of the message, as read_chat / new_replies returned it

#### new_replies

Get new replies. Everything received that has not been read yet, oldest first, each with the `number` of the user's it came in on (reply from that number). By default marks them as read so the next call returns only newer messages.

- `mark_read` boolean, optional (default true)
- `limit` integer, optional (1–500)
- `number` string, optional (1–100 chars): Only what came in on this one of the user's numbers (id, phone or label)

#### delivery_status

Check delivery status. Whether messages were sent, delivered, read or failed.

- `message_ids` uuid[], required (1–200 items)

#### daily_allowance

Daily allowance. What a number may still send: first messages to new contacts today, messages this hour and today, and when the next message can go out. Limits are per number. Without `number`, every number this connection may use.

- `number` string, optional (1–100 chars): One of the user's numbers (id, phone or label)

<a id="connect-limits"></a>

### Sending limits

Every message an AI sends goes through a limiter per number: messages are spaced, capped per hour and per day, and first messages to new contacts are capped by the number's age. These defaults follow published guidance (WhatsApp's "about 200 new customers a day" and the practice of WhatsApp API providers). They were added after an AI tool sent 35 messages in two minutes and the number was banned.

| Protection | Recommended value |
| --- | --- |
| Sending hours | 09:00–17:00 (Africa/Nouakchott) |
| Spacing | 30–90 s between two messages |
| Per hour | 60 messages |
| Per day | 200 messages |
| First messages to new contacts, per day, by the number's age | 20 (days 0–9), 50 (days 10–19), 100 (days 20–29), 200 (from day 30) |
| Quiet start | none in the first 3 days after linking |
| Rest day | replies only after 3 days in a row of first messages |
| Reply rate | first messages pause when fewer than 30% of the last 7 days' got a reply (judged from 8 messages) |
| Copy-paste | the same first message to at most 3 new people a day |
| Opt-out | never message someone who asked to stop |
| Approval | a first message to a new contact needs your OK (`confirmed: true`) |

The organization's **owner** can change or turn off each of them (dashboard → **Connect** → More settings → Sending limits, or `PUT /mcp/safety` signed in; `GET /mcp/safety` shows the current and recommended values). The AI is told the actual values.

> WhatsApp alone decides which numbers it blocks. The recommended values lower the risk; no tool can remove it. Changing them is your choice and your responsibility, and WahaSend is not responsible for a blocked number.

<a id="connect-files"></a>

### Files

- **Open** (`open_attachment`): a photo is shown to the AI (up to 3.75 MB), a PDF or Word file comes back as text (30,000 characters max), a voice note as a transcript, a video as details only. Files up to 10 MB. The file is never stored; each opening is logged without its content. Voice notes use Connect's monthly AI allowance.
- **Send** (`send_attachment`): forward a file from your chats, a public **https** link (up to 16 MB) or bytes from the AI (up to 5 MB). Allowed: photos (JPEG, PNG, WebP), voice notes (OGG/Opus), MP4 and 3GP/MOV videos, PDF, Office and OpenDocument files without macros, RTF, TXT, CSV, GIF, audio. Programs, scripts, web pages, archives and macro files are refused; the first bytes must match the type.
- A file counts like a message for every limit. Forwarding a file someone sent you to another person needs your explicit OK.
- WhatsApp keeps files for a limited time: an old file may answer `ATTACHMENT_EXPIRED`.
