> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nanoclaw.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# WhatsApp

> Connect NanoClaw to your personal WhatsApp with the native Baileys adapter — QR or pairing-code login, shared or dedicated number.

The WhatsApp adapter connects NanoClaw to a personal WhatsApp account as a linked device — no Meta business account, no API key. It's a native adapter built directly on `@whiskeysockets/baileys` 7.0.0-rc.9 (pinned; the WhatsApp Web protocol), not the Chat SDK bridge. For Meta's official Cloud API instead, use `/add-whatsapp-cloud` — see the [channels overview](/channels/overview).

You can run it in two modes:

* **Shared number** — the agent lives on your own WhatsApp account. Its replies are prefixed with the agent name (`Andy: …`) so they're distinguishable from yours.
* **Dedicated number** — a separate phone/SIM for the agent. Setting `ASSISTANT_HAS_OWN_NUMBER=true` drops the name prefix on outbound messages and switches bot-echo detection from prefix matching to sender identity.

## Prerequisites

* A phone with WhatsApp installed and online — you'll link NanoClaw under **Settings → Linked Devices**
* A working NanoClaw install ([quickstart](/quickstart))
* For pairing-code auth: the account's phone number, digits only with country code (e.g. `14155551234`)

## Install

WhatsApp is offered in the first-run setup wizard, or add it later by running `/add-whatsapp` in Claude Code. Both flows do the same thing:

<Steps>
  <Step title="Choose your mode">
    The skill starts with a number safety check: will the agent live on your personal number (shared mode — messages land in your "You" self-chat) or on a separate SIM it owns (dedicated mode)? Dedicated mode writes `ASSISTANT_HAS_OWN_NUMBER=true` to `.env` (and `data/env/env` for containers).
  </Step>

  <Step title="Adapter install">
    The skill copies `src/channels/whatsapp.ts` and the `whatsapp-formatting` container skill from the `channels` branch, wires the adapter into the channel barrel, and installs the pinned Baileys, `qrcode`, and `pino` packages.
  </Step>

  <Step title="Pick an auth method">
    QR code (scan with your phone's camera) or pairing code (enter an 8-character code on your phone — no camera needed). The pairing-code flow asks for your phone number first.
  </Step>

  <Step title="Link your phone">
    A QR code renders (terminal, or a local browser page via the skill) and rotates every \~60 seconds until scanned. Pairing codes also expire in \~60 seconds — enter them immediately under **Linked Devices → Link a Device → Link with phone number instead**. Credentials land in `store/auth/`, and the wizard restarts the service so the adapter picks them up.
  </Step>

  <Step title="Name the agent and get the welcome DM">
    The wizard asks for an agent name (default `Nano`), wires your DM to the first agent group, and sends a welcome message.
  </Step>
</Steps>

To wire more chats or groups later, run `/manage-channels`.

## Platform notes

* **Auth persistence** — credentials live in `store/auth/` (multi-file auth state). Restarts reuse the saved session; you only re-pair if WhatsApp logs the device out. On a server-side logout the adapter wipes `store/auth/` so the next start prompts a fresh pair.
* **Activation** — the adapter only starts when `store/auth/creds.json` exists, `WHATSAPP_PHONE_NUMBER` is set (pairing-code mode), or `WHATSAPP_ENABLED=true` (QR mode). Otherwise it's skipped at startup.
* **Reconnection** — on disconnect the adapter reconnects immediately (with one retry after 5s if that attempt throws) unless it was logged out. Messages sent while disconnected are queued in memory and flushed when the connection reopens.
* **No threads** — the adapter sets `supportsThreads: false`; every inbound message has a null thread ID. Wirings with `per-thread` session mode behave like `shared` here — see the [entity model](/concepts/entity-model).
* **DMs vs groups** — DMs always count as addressed to the agent, in both modes. Group behavior (`…@g.us` JIDs) depends on the mode. **Shared number**: an @-tag of the number addresses you, the human — it never engages the agent; instead the agent triggers when its name appears as a whole word in the message, and unknown senders fall under the `strict` policy so strangers DMing you never spawn agent state. **Dedicated number**: groups engage on an explicit @-mention of the bot's number — either the tap-to-select mention pill, or a typed `@<agent name>` / `@<bot number>` in the text when the message doesn't mention-pill anyone else — and unknown senders get the `request_approval` policy. Group names sync from WhatsApp every 24 hours.
* **Media** — inbound images, video, audio, and documents are downloaded to `data/attachments/` and passed to the agent (unsafe attacker-controlled filenames are replaced). If the direct download fails — typically an expired media URL around a reconnect — the adapter asks WhatsApp to re-upload the media and retries; if it still can't be fetched, the message reaches the agent with a visible `[<type> could not be downloaded]` note instead of silently dropping the attachment. Outbound files are sent as native WhatsApp media by extension, with the reply text as the first file's caption.
* **Formatting** — the agent's markdown is converted to WhatsApp formatting (`**bold**` → `*bold*`, headings → bold, links → `text (url)`); code blocks pass through untouched. `@<phone>` in replies becomes a real tappable mention.
* **Interactive questions** — `ask_user_question` renders as text with slash-command answers (`/approve`, `/reject`); the adapter matches your reply to the pending question. Editing and deleting sent messages isn't supported (linked-device limitation).
* **Self-chat** — in shared mode you can message the agent in your own "You" chat. The adapter tells your typed messages apart from its own echoes via a sent-message cache.

## Troubleshooting

* **Logged out / re-pair loop** — if logs show `WhatsApp logged out` followed by `WhatsApp auth cleared`, the account was unlinked server-side (often a 401). The adapter already wiped `store/auth/`; set `WHATSAPP_ENABLED=true` and restart (or re-run `/add-whatsapp`) to scan a fresh QR.
* **`Could not fetch current WhatsApp Web version`** — Baileys' hardcoded protocol version goes stale and WhatsApp rejects it (405), so the adapter fetches the current version at startup from wppconnect.io with web.whatsapp.com as fallback. This error means both fetches failed: check outbound network access to those hosts.
* **Agent silent, messages arrive late in a burst** — the connection dropped; outbound messages were queued (`WA disconnected, message queued` in logs) and flushed on reconnect. Confirm the last `Connected to WhatsApp` log line and that only **one** NanoClaw process holds these credentials — a second instance causes "conflict" disconnect loops.
* **QR or pairing code expired** — both rotate/expire in \~60 seconds. Re-run the auth step for a fresh code; for pairing codes, the number must be digits only with country code, no `+`. If pairing codes keep getting rejected, switch to the QR method — it's more reliable.

For service-level checks (logs, restarts, wiring queries), see [troubleshooting](/operate/troubleshooting).
