Skip to main content
Glama
README.md
# WhatsApp MCP Platform (core engine)

Link a WhatsApp number by QR code, send/read messages over a REST API, and
expose the same connection to Claude (or any MCP client) as a remote MCP
server -- the core engine behind a "Blueticks-style" product.

This is the **core engine only**: QR linking, the REST API, and the MCP
server. No dashboard UI, auth beyond API keys, or billing yet -- see
"Turning this into a sellable product" below for what to add next.

## How it fits together

```
Customer's phone  <-- QR / WhatsApp Web protocol -->  Baileys socket (per account)
                                                              |
                                                    in-memory chat/message store
                                                              |
                                    -----------------------------------------
                                    |                                       |
                              REST API (/qr, /send,                  MCP server (/mcp)
                              /chats, /messages, ...)                 tools: whatsapp_status,
                              -- for your own dashboard/scripts       whatsapp_get_qr,
                                                                       whatsapp_send_message,
                                                                       whatsapp_list_chats,
                                                                       whatsapp_read_messages
```

Each customer = one "account": a WhatsApp connection identified by an API
key. You (the operator) create accounts with your `MASTER_KEY`; each
customer's key then scopes every REST and MCP call to *their* connection
only -- one account can never see another's messages.

WhatsApp connectivity is via [Baileys](https://github.com/WhiskeySockets/Baileys),
an open-source library that speaks the WhatsApp Web protocol directly (the
same QR-linking flow as web.whatsapp.com). **This is not the official
WhatsApp Business API** -- see "Important: ToS and ban risk" below.

## Setup

```bash
npm install
cp .env.example .env
# edit .env and set a real MASTER_KEY

npm run build
npm start
# or, for auto-reload during development:
npm run dev
```

The server listens on `PORT` (default 3000) and logs the admin and MCP URLs
on startup.

## Provisioning a customer (admin API)

All `/admin/*` routes require `Authorization: Bearer <MASTER_KEY>`.

```bash
# Create an account -- do this once per customer, store the apiKey for them
curl -X POST http://localhost:3000/admin/accounts \
  -H "Authorization: Bearer $MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Customer Name"}'
# -> { "id": "...", "name": "...", "apiKey": "wap_live_...", "createdAt": "..." }

# List accounts (status only -- apiKey is only ever shown once, at creation)
curl http://localhost:3000/admin/accounts -H "Authorization: Bearer $MASTER_KEY"

# Remove an account (logs the number out and deletes its session)
curl -X DELETE http://localhost:3000/admin/accounts/<id> -H "Authorization: Bearer $MASTER_KEY"
```

## Linking a WhatsApp number (customer's own key)

```bash
# Start the connection and get a QR code (base64 PNG data URL)
curl http://localhost:3000/qr -H "Authorization: Bearer $CUSTOMER_API_KEY"
# -> { "status": "qr_pending", "qrAvailable": true, "qrDataUrl": "data:image/png;base64,..." }
```

Render `qrDataUrl` as an `<img>` in a dashboard, or decode and display it
any other way. Have the customer scan it from their phone:
**WhatsApp > Settings > Linked Devices > Link a device**. Poll `/status`
(or `/qr` again) until `status` becomes `"connected"`.

## REST API reference

All routes below (except `/admin/*` and `/health`) require
`Authorization: Bearer <customer apiKey>`.

| Method & path             | Purpose                                      |
| -------------------------- | --------------------------------------------- |
| `GET /me`                  | Account info + current status                |
| `POST /connect`            | Start/resume the connection if it's not live |
| `GET /qr`                  | Current status + QR code (if pending)         |
| `GET /status`              | Current status only                           |
| `POST /send`               | `{ "to": "+1555...", "message": "..." }`      |
| `GET /chats?limit=20`      | Recently active chats                         |
| `GET /messages?chatId=...&limit=30` | Recent messages in one chat           |
| `POST /logout`             | Unlink the number and clear its session       |

## Connecting Claude (or any MCP client)

The MCP endpoint is `POST {your-server-url}/mcp`, authenticated the same
way as the REST API: `Authorization: Bearer <customer apiKey>`.

In Claude: **Settings/Customize -> Connectors -> Add custom connector**,
paste your server's `/mcp` URL. If Claude's custom-connector UI doesn't
prompt for a bearer token directly, front the endpoint with a URL-embedded
token variant or an OAuth shim -- the stateless handler in
`src/mcp/server.ts` is the piece to adapt if you want a different auth
scheme (e.g. OAuth, like Blueticks' remote connector).

Tools exposed: `whatsapp_status`, `whatsapp_get_qr`, `whatsapp_send_message`,
`whatsapp_list_chats`, `whatsapp_read_messages`.

## Important: ToS and ban risk

Baileys (and every similar tool, including the third-party services we
discussed) connects as an *unofficial* WhatsApp Web client, not Meta's
Business API. WhatsApp's terms don't sanction third-party automation like
this, so any number connected this way carries some risk of being flagged
or restricted -- more so with high message volume or spammy behavior. If
you productize this:

- Say so plainly in your terms of service; don't imply Meta affiliation or
  endorsement.
- Rate-limit sends per account and avoid enabling bulk/unsolicited
  messaging patterns -- that's both the biggest ban trigger and the
  behavior most likely to draw abuse complaints against your platform.
- Consider offering (or migrating power users to) the real WhatsApp
  Business API for anyone sending real volume.

## Known MVP limitations (by design, for a first cut)

- **In-memory chat/message store.** Restarting the process clears chat
  history seen so far (linked sessions persist on disk in `sessions/` and
  reconnect automatically -- only the message *cache* is volatile). Swap in
  Postgres/SQLite before real usage.
- **No dashboard.** Account creation and QR display are API-only; build a
  small frontend against `/admin/*` and `/qr`.
- **No billing/metering.** Nothing tracks usage per account yet.
- **Single process.** Fine for one server; for horizontal scaling you'd
  move account runtime state (and the Baileys auth state) into shared
  storage instead of in-process `Map`s.
- **Text messages only.** Sending/reading media, groups-specific actions,
  and reactions aren't wired up yet, though Baileys supports all of it.

## Turning this into a sellable product

Roughly in priority order:
1. Swap the JSON-file store and in-memory chat cache for a real database.
2. Add a minimal dashboard: create account, show QR, show status, revoke.
3. Add per-account rate limiting and usage metering (for billing + abuse
   prevention).
4. Add Stripe (or similar) for subscriptions/usage-based billing.
5. Decide your auth story for the MCP connector specifically -- API-key
   Bearer (current, simplest) vs. OAuth (nicer UX, more setup, closer to
   what Blueticks/Wassenger do).