Skip to content

WhatsApp

WhatsApp integration uses a QR-code pairing flow (the same one WhatsApp Web uses) and runs in a subprocess worker to keep Baileys’ global state isolated from the main Electron process.

Open Settings → Messaging

In Craft Agent, click the WhatsApp tile’s Connect button.

Scan the QR code with your phone

A QR code appears in the dialog. On your phone, open WhatsApp → Settings → Linked Devices → Link a Device and scan the code.

Wait for connection

The dialog updates to Connected as <your-name>. Your Baileys session is persisted in:

~/.craft-agent/workspaces/{workspaceId}/messaging/whatsapp-session/

Keep this folder — deleting it forces a re-pair.

Message yourself on WhatsApp

Open a chat with your own number (the “Message yourself” entry in WhatsApp’s New Chat screen).

Send /new

The worker sees the message from your own JID, creates a session, and binds the chat. Replies from the agent show up prefixed with 🤖 so you can tell them apart from your own messages.

Chat normally

Everything you type is forwarded to the agent. The 🤖 prefix on replies is also how the worker filters its own echoes — so don’t strip it if you copy/repost.

When enabled (default), messages you send from another device on the same WhatsApp account to your own JID are treated as inbound and routed to the bound session.

BehaviourWhy it matters
Reply prefixAll outbound messages from the worker to self are prefixed with 🤖 (U+1F916). This is how the worker filters its own echoes so it doesn’t drive itself in an infinite loop.
Sent-ID trackingThe worker also tracks message IDs it sent, so replies to those are handled as context rather than fresh prompts.
LID supportLong-ID contacts (the new lid format WhatsApp rolls out gradually) are recognized as “self” when they map to your account.

Toggle self-chat off in Settings → Messaging → WhatsApp if you prefer to drive sessions only from a separate contact.

Supported media types: photos, documents, voice messages, video, audio. Same 20 MB cap as Telegram. Files land in the session as FileAttachment objects with the original MIME type.

For WhatsApp bindings, approvalChannel is always app — the gateway doesn’t support inline approval replies on WhatsApp. When a bound session is in Ask mode and the agent requests approval for a bash command, the prompt appears in the desktop app, not in the chat. Everything else (prompts, replies) flows through WhatsApp as normal.

When the agent is in Explore mode and submits a plan via SubmitPlan, WhatsApp users see a text-only pointer:

📝 A plan is ready for review. Open the desktop app to inspect and approve it.

You cannot accept or reject a plan from WhatsApp — the approval round-trip happens in the desktop app only.

Telegram bindings, by contrast, get two tappable buttons (✅ Accept plan and ♻️ Accept & compact) attached to the plan message, plus the plan content inline (or as an attached plan.md if it exceeds Telegram’s message length cap).

The WhatsApp adapter is unusual because Baileys holds a lot of global state (credential stores, message queues, reconnect timers) and expects to be the only instance in its process. Running it inside the main Electron process would make crashes contagious. Instead:

┌──────────────────────────┐ NDJSON over stdio ┌───────────────────────────┐
│ Electron main process │ ────────────────────► │ messaging-whatsapp-worker │
│ (messaging-gateway │ ◄──────────────────── │ (Baileys + grammY-like │
│ adapter client) │ typed events │ event loop, single CJS) │
└──────────────────────────┘ └───────────────────────────┘
↑ ↑
│ │
└─ sends IncomingMessage └─ persists creds to
to Router whatsapp-session/
  • The worker is a single bundled CJS file (packages/messaging-whatsapp-worker/dist) launched under Electron’s embedded Node via ELECTRON_RUN_AS_NODE.
  • Communication is newline-delimited JSON over stdio — requests from the main process, typed events (QR, connected, message, disconnect) from the worker.
  • On worker exit, the main process drains pending sends with a timeout so nothing is silently dropped.
  • In CI, the worker bundle is built and verified in release artifacts so broken bundles can’t ship.
QR code shows but never connects
  • Make sure the phone that scans is online.
  • Check that WhatsApp on the phone is up to date — Meta sometimes rotates the linking protocol and Baileys takes a release to catch up.
  • Try Disconnect from the three-dot menu and re-pair from scratch.
'Disconnect' vs 'Forget Device'

The menu label is Disconnect (renamed from the earlier “Forget Device”). It clears the persisted Baileys session and forces a re-pair on next connect.

Agent replies appear on the phone but not on the desktop WhatsApp

WhatsApp syncs linked devices lazily. Open WhatsApp on the paired device, wait 10–15 seconds, or send a dummy message from the phone to force a sync.

Pending messages lost when I quit the app

The worker drains pending outbound sends on exit with a timeout. If the app is force-killed the queue is lost, but all inbound messages are persisted on WhatsApp’s side — you won’t miss anything you sent to the agent; you may miss agent replies that were mid-flight.

Self-chat isn't recognized
  • Confirm self-chat mode is enabled in Settings → Messaging → WhatsApp.
  • If your account recently migrated to a LID, the binding may still reference the old JID. Unbind and re-bind from the self-chat.
Linked device was kicked by WhatsApp

Meta periodically expires inactive linked devices. Re-pair from the app — your bindings and session data are preserved.

Can't accept a plan from WhatsApp

By design, for now. When the agent submits a plan in Explore mode, WhatsApp bindings get a text pointer to “open the desktop app” instead of an interactive button. See Plan Submission for the reasons and workarounds.