WhatsApp (web channel)

Trạng thái: Chỉ hỗ trợ WhatsApp Web qua Baileys. Gateway quản lý session(s).

Cài đặt nhanh (người mới)

  1. Dùng số điện thoại riêng nếu có thể (khuyên dùng).
  2. Cấu hình WhatsApp trong ~/.openclaw/openclaw.json.
  3. Chạy openclaw channels login để quét mã QR (Linked Devices).
  4. Khởi động gateway.

Config tối thiểu:

{
  channels: {
    whatsapp: {
      dmPolicy: "allowlist",
      allowFrom: ["+15551234567"],
    },
  },
}

Mục tiêu

  • Nhiều tài khoản WhatsApp (multi-account) trong một tiến trình Gateway.
  • Routing xác định: các reply quay về WhatsApp, không routing qua model.
  • Model nhìn thấy đủ context để hiểu quoted replies.

Ghi config

Mặc định, WhatsApp được phép ghi cập nhật config khi dùng /config set|unset (yêu cầu commands.config: true).

Tắt tính năng này:

{
  channels: { whatsapp: { configWrites: false } },
}

Kiến trúc (ai sở hữu cái gì)

  • Gateway sở hữu Baileys socket và inbox loop.
  • CLI / macOS app giao tiếp với gateway; không dùng Baileys trực tiếp.
  • Active listener bắt buộc để gửi tin nhắn ra ngoài; nếu không có sẽ fail ngay.

Lấy số điện thoại (hai chế độ)

WhatsApp yêu cầu số di động thật để xác minh. Số VoIP và số ảo thường bị chặn. Có hai cách được hỗ trợ để chạy OpenClaw trên WhatsApp:

Số riêng biệt (khuyên dùng)

Dùng số điện thoại riêng cho OpenClaw. Trải nghiệm tốt nhất, routing rõ ràng, không có vấn đề self-chat. Cài đặt lý tưởng: điện thoại Android dự phòng/cũ + eSIM. Để máy kết nối Wi‑Fi và sạc điện, rồi liên kết qua mã QR.

WhatsApp Business: Các bạn có thể dùng WhatsApp Business trên cùng thiết bị với số khác. Rất tốt để tách biệt WhatsApp cá nhân — cài WhatsApp Business và đăng ký số OpenClaw ở đó.

Config mẫu (số riêng, allowlist một người dùng):

{
  channels: {
    whatsapp: {
      dmPolicy: "allowlist",
      allowFrom: ["+15551234567"],
    },
  },
}

Chế độ pairing (tùy chọn): Nếu muốn dùng pairing thay vì allowlist, đặt channels.whatsapp.dmPolicy thành pairing. Người gửi lạ sẽ nhận mã pairing; phê duyệt bằng: openclaw pairing approve whatsapp <code>

Số cá nhân (dự phòng)

Cách dự phòng nhanh: chạy OpenClaw trên số của chính bạn. Nhắn tin cho chính mình (WhatsApp “Message yourself”) để test mà không spam liên hệ. Chuẩn bị đọc mã xác minh trên điện thoại chính khi cài đặt và thử nghiệm. Phải bật chế độ self-chat. Khi wizard hỏi số WhatsApp cá nhân, nhập số điện thoại mà bạn sẽ nhắn tin từ đó (owner/sender), không phải số assistant.

Config mẫu (số cá nhân, self-chat):

{
  "whatsapp": {
    "selfChatMode": true,
    "dmPolicy": "allowlist",
    "allowFrom": ["+15551234567"]
  }
}

Các reply trong self-chat mặc định dùng [{identity.name}] khi được đặt (nếu không thì dùng [openclaw]) nếu messages.responsePrefix chưa được đặt. Đặt rõ ràng để tùy chỉnh hoặc tắt prefix (dùng "" để xóa).

Mẹo tìm số

  • eSIM địa phương từ nhà mạng di động trong nước (đáng tin cậy nhất)
  • SIM trả trước — rẻ, chỉ cần nhận một tin SMS để xác minh

Tránh: TextNow, Google Voice, hầu hết dịch vụ “SMS miễn phí” — WhatsApp chặn các dịch vụ này rất mạnh.

Mẹo: Số chỉ cần nhận một tin SMS xác minh. Sau đó, các session WhatsApp Web được lưu qua creds.json.

Tại sao không dùng Twilio?

  • Các bản OpenClaw đầu hỗ trợ tích hợp WhatsApp Business của Twilio.
  • Số WhatsApp Business không phù hợp cho trợ lý cá nhân.
  • Meta áp dụng cửa sổ trả lời 24 giờ; nếu bạn chưa trả lời trong 24 giờ qua, số business không thể gửi tin nhắn mới.
  • Sử dụng nhiều hoặc “nói chuyện nhiều” kích hoạt chặn mạnh, vì tài khoản business không được thiết kế để gửi hàng chục tin nhắn trợ lý cá nhân.
  • Kết quả: gửi không ổn định và bị chặn thường xuyên, nên đã gỡ hỗ trợ.

Đăng nhập + thông tin xác thực

  • Lệnh đăng nhập: openclaw channels login (QR qua Linked Devices).
  • Đăng nhập nhiều tài khoản: openclaw channels login --account <id> (<id> = accountId).
  • Tài khoản mặc định (khi bỏ qua --account): default nếu có, nếu không thì account id đầu tiên được cấu hình (đã sắp xếp).
  • Thông tin xác thực lưu trong ~/.openclaw/credentials/whatsapp/<accountId>/creds.json.
  • Bản sao lưu tại creds.json.bak (khôi phục khi bị hỏng).
  • Tương thích cũ: các cài đặt cũ lưu file Baileys trực tiếp trong ~/.openclaw/credentials/.
  • Đăng xuất: openclaw channels logout (hoặc --account <id>) xóa trạng thái xác thực WhatsApp (nhưng giữ oauth.json dùng chung).
  • Socket đã đăng xuất => lỗi yêu cầu liên kết lại.

Luồng tin nhắn đến (DM + nhóm)

  • Sự kiện WhatsApp đến từ messages.upsert (Baileys).
  • Inbox listeners được tách ra khi tắt để tránh tích lũy event handlers trong tests/restarts.
  • Chat trạng thái/broadcast bị bỏ qua.
  • Chat trực tiếp dùng E.164; nhóm dùng group JID.
  • Chính sách DM: channels.whatsapp.dmPolicy kiểm soát quyền truy cập chat trực tiếp (mặc định: pairing).
    • Pairing: người gửi lạ nhận mã pairing (phê duyệt qua openclaw pairing approve whatsapp <code>; mã hết hạn sau 1 giờ).
    • Open: yêu cầu channels.whatsapp.allowFrom bao gồm "*".
    • Số WhatsApp đã liên kết của bạn được tin tưởng ngầm định, nên tin nhắn tự gửi bỏ qua kiểm tra channels.whatsapp.dmPolicychannels.whatsapp.allowFrom.

Chế độ số cá nhân (dự phòng)

Nếu chạy OpenClaw trên số WhatsApp cá nhân, bật channels.whatsapp.selfChatMode (xem config mẫu ở trên).

Hành vi:

  • DM gửi đi không bao giờ kích hoạt reply pairing (tránh spam liên hệ).
  • Người gửi lạ đến vẫn tuân theo channels.whatsapp.dmPolicy.
  • Chế độ self-chat (allowFrom bao gồm số của bạn) tránh read receipts tự động và bỏ qua mention JIDs.
  • Read receipts được gửi cho DM không phải self-chat.

Read receipts

Mặc định, gateway đánh dấu tin nhắn WhatsApp đến là đã đọc (tích xanh) khi được chấp nhận.

Tắt toàn cục:

{
  channels: { whatsapp: { sendReadReceipts: false } },
}

Tắt theo tài khoản:

{
  channels: {
    whatsapp: {
      accounts: {
        personal: { sendReadReceipts: false },
      },
    },
  },
}

Lưu ý:

  • Chế độ self-chat luôn bỏ qua read receipts.

WhatsApp FAQ: gửi tin nhắn + pairing

OpenClaw có nhắn tin cho liên hệ ngẫu nhiên khi tôi liên kết WhatsApp không? Không. Chính sách DM mặc định là pairing, nên người gửi lạ chỉ nhận mã pairing và tin nhắn của họ không được xử lý. OpenClaw chỉ trả lời các chat mà nó nhận được, hoặc các tin nhắn bạn kích hoạt rõ ràng (agent/CLI).

Pairing hoạt động như thế nào trên WhatsApp? Pairing là cổng DM cho người gửi lạ:

  • DM đầu tiên từ người gửi mới trả về mã ngắn (tin nhắn không được xử lý).
  • Phê duyệt bằng: openclaw pairing approve whatsapp <code> (liệt kê bằng openclaw pairing list whatsapp).
  • Mã hết hạn sau 1 giờ; yêu cầu chờ giới hạn 3 mã mỗi channel.

Nhiều người có thể dùng các instance OpenClaw khác nhau trên một số WhatsApp không? Có, bằng cách routing mỗi người gửi đến một agent khác qua bindings (peer kind: "dm", sender E.164 như +15551234567). Các reply vẫn đến từ cùng tài khoản WhatsApp, và chat trực tiếp gộp vào session chính của mỗi agent, nên dùng một agent cho mỗi người. Kiểm soát truy cập DM (dmPolicy/allowFrom) là toàn cục cho mỗi tài khoản WhatsApp. Xem Multi-Agent Routing.

Tại sao wizard hỏi số điện thoại của tôi? Wizard dùng nó để đặt allowlist/owner của bạn để DM của chính bạn được cho phép. Nó không được dùng để tự động gửi. Nếu chạy trên số WhatsApp cá nhân, dùng cùng số đó và bật channels.whatsapp.selfChatMode.

Chuẩn hóa tin nhắn (model nhìn thấy gì)

  • Body là nội dung tin nhắn hiện tại với envelope.
  • Context quoted reply luôn được thêm vào:
    [Replying to +1555 id:ABC123]
    <quoted text or <media:...>>
    [/Replying]
  • Reply metadata cũng được đặt:
    • ReplyToId = stanzaId
    • ReplyToBody = quoted body hoặc media placeholder
    • ReplyToSender = E.164 khi biết
  • Tin nhắn đến chỉ có media dùng placeholders:
    • <media:image|video|audio|document|sticker>

Nhóm

  • Nhóm map tới agent:<agentId>:whatsapp:group:<jid> sessions.
  • Chính sách nhóm: channels.whatsapp.groupPolicy = open|disabled|allowlist (mặc định allowlist).
  • Chế độ kích hoạt:
    • mention (mặc định): yêu cầu @mention hoặc regex match.
    • always: luôn kích hoạt.
  • /activation mention|always chỉ dành cho owner và phải gửi như tin nhắn độc lập.
  • Owner = channels.whatsapp.allowFrom (hoặc self E.164 nếu chưa đặt).
  • History injection (chỉ pending):
    • Tin nhắn gần đây chưa xử lý (mặc định 50) được chèn dưới: [Chat messages since your last reply - for context] (tin nhắn đã có trong session không được chèn lại)
    • Tin nhắn hiện tại dưới: [Current message - respond to this]
    • Hậu tố người gửi được thêm: [from: Name (+E164)]
  • Group metadata được cache 5 phút (subject + participants).

Gửi reply (threading)

  • WhatsApp Web gửi tin nhắn chuẩn (không có quoted reply threading trong gateway hiện tại).
  • Reply tags bị bỏ qua trên channel này.

Acknowledgment reactions (tự động react khi nhận)

WhatsApp có thể tự động gửi emoji reactions cho tin nhắn đến ngay khi nhận, trước khi bot tạo reply. Điều này cung cấp phản hồi tức thì cho người dùng rằng tin nhắn đã được nhận.

Cấu hình:

{
  "whatsapp": {
    "ackReaction": {
      "emoji": "👀",
      "direct": true,
      "group": "mentions"
    }
  }
}

Tùy chọn:

  • emoji (string): Emoji dùng để xác nhận (ví dụ: ”👀”, ”✅”, ”📨”). Để trống hoặc bỏ qua = tắt tính năng.
  • direct (boolean, mặc định: true): Gửi reactions trong chat trực tiếp/DM.
  • group (string, mặc định: "mentions"): Hành vi chat nhóm:
    • "always": React tất cả tin nhắn nhóm (ngay cả không có @mention)
    • "mentions": Chỉ react khi bot được @mentioned
    • "never": Không bao giờ react trong nhóm

Ghi đè theo tài khoản:

{
  "whatsapp": {
    "accounts": {
      "work": {
        "ackReaction": {
          "emoji": "✅",
          "direct": false,
          "group": "always"
        }
      }
    }
  }
}

Lưu ý hành vi:

  • Reactions được gửi ngay lập tức khi nhận tin nhắn, trước typing indicators hoặc bot replies.
  • Trong nhóm với requireMention: false (activation: always), group: "mentions" sẽ react tất cả tin nhắn (không chỉ @mentions).
  • Fire-and-forget: lỗi reaction được ghi log nhưng không ngăn bot trả lời.
  • Participant JID tự động được bao gồm cho group reactions.
  • WhatsApp bỏ qua messages.ackReaction; dùng channels.whatsapp.ackReaction thay thế.

Agent tool (reactions)

  • Tool: whatsapp với action react (chatJid, messageId, emoji, tùy chọn remove).
  • Tùy chọn: participant (người gửi nhóm), fromMe (react tin nhắn của chính bạn), accountId (multi-account).
  • Ngữ nghĩa xóa reaction: xem /tools/reactions.
  • Tool gating: channels.whatsapp.actions.reactions (mặc định: enabled).

Giới hạn

  • Text gửi đi được chia thành channels.whatsapp.textChunkLimit (mặc định 4000).
  • Chia theo dòng mới tùy chọn: đặt channels.whatsapp.chunkMode="newline" để chia theo dòng trống (ranh giới đoạn văn) trước khi chia theo độ dài.
  • Lưu media đến giới hạn bởi channels.whatsapp.mediaMaxMb (mặc định 50 MB).
  • Media items gửi đi giới hạn bởi agents.defaults.mediaMaxMb (mặc định 5 MB).

Gửi đi (text + media)

  • Dùng active web listener; lỗi nếu gateway không chạy.
  • Chia text: tối đa 4k mỗi tin nhắn (có thể cấu hình qua channels.whatsapp.textChunkLimit, tùy chọn channels.whatsapp.chunkMode).
  • Media:
    • Hỗ trợ Image/video/audio/document.
    • Audio gửi dưới dạng PTT; audio/ogg => audio/ogg; codecs=opus.
    • Caption chỉ trên media item đầu tiên.
    • Media fetch hỗ trợ HTTP(S) và đường dẫn local.
    • GIF động: WhatsApp mong đợi MP4 với gifPlayback: true để lặp inline.
      • CLI: openclaw message send --media <mp4> --gif-playback
      • Gateway: params send bao gồm gifPlayback: true

Voice notes (PTT audio)

WhatsApp gửi audio dưới dạng voice notes (bong bóng PTT).

  • Kết quả tốt nhất: OGG/Opus. OpenClaw viết lại audio/ogg thành audio/ogg; codecs=opus.
  • [[audio_as_voice]] bị bỏ qua cho WhatsApp (audio đã được gửi dưới dạng voice note).

Giới hạn media + tối ưu hóa

  • Giới hạn gửi đi mặc định: 5 MB (mỗi media item).
  • Ghi đè: agents.defaults.mediaMaxMb.
  • Hình ảnh tự động tối ưu hóa thành JPEG dưới giới hạn (resize + quality sweep).
  • Media quá cỡ => lỗi; media reply fallback về text warning.

Heartbeats

  • Gateway heartbeat ghi log sức khỏe kết nối (web.heartbeatSeconds, mặc định 60s).
  • Agent heartbeat có thể cấu hình theo agent (agents.list[].heartbeat) hoặc toàn cục qua agents.defaults.heartbeat (fallback khi không có entry theo agent).
    • Dùng heartbeat prompt đã cấu hình (mặc định: Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.) + hành vi bỏ qua HEARTBEAT_OK.
    • Gửi mặc định đến channel được dùng lần cuối (hoặc target đã cấu hình).

Hành vi kết nối lại

  • Chính sách backoff: web.reconnect:
    • initialMs, maxMs, factor, jitter, maxAttempts.
  • Nếu đạt maxAttempts, web monitoring dừng (degraded).
  • Đã đăng xuất => dừng và yêu cầu liên kết lại.

Bản đồ config nhanh

  • channels.whatsapp.dmPolicy (Chính sách DM: pairing/allowlist/open/disabled).
  • channels.whatsapp.selfChatMode (cài đặt cùng số; bot dùng số WhatsApp cá nhân của bạn).
  • channels.whatsapp.allowFrom (DM allowlist). WhatsApp dùng số điện thoại E.164 (không có username).
  • channels.whatsapp.mediaMaxMb (giới hạn lưu media đến).
  • channels.whatsapp.ackReaction (tự động reaction khi nhận tin nhắn: {emoji, direct, group}).
  • channels.whatsapp.accounts.<accountId>.* (cài đặt theo tài khoản + authDir tùy chọn).
  • channels.whatsapp.accounts.<accountId>.mediaMaxMb (giới hạn media đến theo tài khoản).
  • channels.whatsapp.accounts.<accountId>.ackReaction (ghi đè ack reaction theo tài khoản).
  • channels.whatsapp.groupAllowFrom (group sender allowlist).
  • channels.whatsapp.groupPolicy (chính sách nhóm).
  • channels.whatsapp.historyLimit / channels.whatsapp.accounts.<accountId>.historyLimit (group history context; 0 tắt).
  • channels.whatsapp.dmHistoryLimit (giới hạn lịch sử DM theo lượt người dùng). Ghi đè theo người dùng: channels.whatsapp.dms["<phone>"].historyLimit.
  • channels.whatsapp.groups (group allowlist + mention gating mặc định; dùng "*" để cho phép tất cả)
  • channels.whatsapp.actions.reactions (gate WhatsApp tool reactions).
  • agents.list[].groupChat.mentionPatterns (hoặc messages.groupChat.mentionPatterns)
  • messages.groupChat.historyLimit
  • channels.whatsapp.messagePrefix (prefix đến; theo tài khoản: channels.whatsapp.accounts.<accountId>.messagePrefix; deprecated: messages.messagePrefix)
  • messages.responsePrefix (prefix gửi đi)
  • agents.defaults.mediaMaxMb
  • agents.defaults.heartbeat.every
  • agents.defaults.heartbeat.model (ghi đè tùy chọn)
  • agents.defaults.heartbeat.target
  • agents.defaults.heartbeat.to
  • agents.defaults.heartbeat.session
  • agents.list[].heartbeat.* (ghi đè theo agent)
  • session.* (scope, idle, store, mainKey)
  • web.enabled (tắt channel startup khi false)
  • web.heartbeatSeconds
  • web.reconnect.*

Logs + troubleshooting

  • Subsystems: whatsapp/inbound, whatsapp/outbound, web-heartbeat, web-reconnect.
  • File log: /tmp/openclaw/openclaw-YYYY-MM-DD.log (có thể cấu hình).
  • Hướng dẫn troubleshooting: Gateway troubleshooting.

Troubleshooting (nhanh)

Chưa liên kết / yêu cầu đăng nhập QR

  • Triệu chứng: channels status hiển thị linked: false hoặc cảnh báo “Not linked”.
  • Sửa: chạy openclaw channels login trên gateway host và quét QR (WhatsApp → Settings → Linked Devices).

Đã liên kết nhưng ngắt kết nối / vòng lặp kết nối lại

  • Triệu chứng: channels status hiển thị running, disconnected hoặc cảnh báo “Linked but disconnected”.
  • Sửa: openclaw doctor (hoặc khởi động lại gateway). Nếu vẫn tiếp tục, liên kết lại qua channels login và kiểm tra openclaw logs --follow.

Bun runtime

  • Bun không được khuyên dùng. WhatsApp (Baileys) và Telegram không ổn định trên Bun. Chạy gateway với Node. (Xem ghi chú runtime trong Getting Started.)