Zalo personal
Status: experimental. This integration automates a personal Zalo account via native zca-js inside OpenClaw.
Bundled plugin
Section titled “Bundled plugin”Zalo Personal ships as a bundled plugin in current OpenClaw releases, so normal packaged builds do not need a separate install.
If you are on an older build or a custom install that excludes Zalo Personal, install the npm package directly:
- Install via CLI:
openclaw plugins install @openclaw/zalouser - Pinned version:
openclaw plugins install @openclaw/[email protected] - Or from a source checkout:
openclaw plugins install ./path/to/local/zalouser-plugin - Details: Plugins
No external zca/openzca CLI binary is required.
Quick setup (beginner)
Section titled “Quick setup (beginner)”- Ensure the Zalo Personal plugin is available.
- Current packaged OpenClaw releases already bundle it.
- Older/custom installs can add it manually with the commands above.
- Login (QR, on the Gateway machine):
openclaw channels login --channel zalouser- Scan the QR code with the Zalo mobile app.
- Enable the channel:
{ channels: { zalouser: { enabled: true, dmPolicy: "pairing", }, },}- Restart the Gateway (or finish setup).
- DM access defaults to pairing; approve the pairing code on first contact.
What it is
Section titled “What it is”- Runs entirely in-process via
zca-js. - Uses native event listeners to receive inbound messages.
- Sends replies directly through the JS API (text/media/link).
- Designed for “personal account” use cases where Zalo Bot API is not available.
Naming
Section titled “Naming”Channel id is zalouser to make it explicit this automates a personal Zalo user account (unofficial). We keep zalo reserved for a potential future official Zalo API integration.
Finding IDs (directory)
Section titled “Finding IDs (directory)”Use the directory CLI to discover peers/groups and their IDs:
openclaw directory self --channel zalouseropenclaw directory peers list --channel zalouser --query "name"openclaw directory groups list --channel zalouser --query "work"Limits
Section titled “Limits”- Outbound text is chunked to ~2000 characters (Zalo client limits).
- Streaming is blocked by default.
Access control (DMs)
Section titled “Access control (DMs)”channels.zalouser.dmPolicy supports: pairing | allowlist | open | disabled (default: pairing).
channels.zalouser.allowFrom should use stable Zalo user IDs. It can also reference static sender access groups (accessGroup:<name>). During interactive setup, entered names can be resolved to IDs using the plugin’s in-process contact lookup.
If a raw name remains in config, startup resolves it only when channels.zalouser.dangerouslyAllowNameMatching: true is enabled. Without that opt-in, runtime sender checks are ID-only and raw names are ignored for authorization.
Approve via:
openclaw pairing list zalouseropenclaw pairing approve zalouser <code>
Group access (optional)
Section titled “Group access (optional)”- Default:
channels.zalouser.groupPolicy = "open"(groups allowed). Usechannels.defaults.groupPolicyto override the default when unset. - Restrict to an allowlist with:
channels.zalouser.groupPolicy = "allowlist"channels.zalouser.groups(keys should be stable group IDs; names are resolved to IDs on startup only whenchannels.zalouser.dangerouslyAllowNameMatching: trueis enabled)channels.zalouser.groupAllowFrom(controls which senders in allowed groups can trigger the bot; static sender access groups can be referenced withaccessGroup:<name>)
- Block all groups:
channels.zalouser.groupPolicy = "disabled". - The configure wizard can prompt for group allowlists.
- On startup, OpenClaw resolves group/user names in allowlists to IDs and logs the mapping only when
channels.zalouser.dangerouslyAllowNameMatching: trueis enabled. - Group allowlist matching is ID-only by default. Unresolved names are ignored for auth unless
channels.zalouser.dangerouslyAllowNameMatching: trueis enabled. channels.zalouser.dangerouslyAllowNameMatching: trueis a break-glass compatibility mode that re-enables mutable startup name resolution and runtime group-name matching.- If
groupAllowFromis unset, runtime falls back toallowFromfor group sender checks. - Sender checks apply to both normal group messages and control commands (for example
/new,/reset).
Example:
{ channels: { zalouser: { groupPolicy: "allowlist", groupAllowFrom: ["1471383327500481391"], groups: { "123456789": { allow: true }, "Work Chat": { allow: true }, }, }, },}Group mention gating
Section titled “Group mention gating”channels.zalouser.groups.<group>.requireMentioncontrols whether group replies require a mention.- Resolution order: exact group id/name -> normalized group slug ->
*-> default (true). - This applies both to allowlisted groups and open group mode.
- Quoting a bot message counts as an implicit mention for group activation.
- Authorized control commands (for example
/new) can bypass mention gating. - When a group message is skipped because mention is required, OpenClaw stores it as pending group history and includes it on the next processed group message.
- Group history limit defaults to
messages.groupChat.historyLimit(fallback50). You can override per account withchannels.zalouser.historyLimit.
Example:
{ channels: { zalouser: { groupPolicy: "allowlist", groups: { "*": { allow: true, requireMention: true }, "Work Chat": { allow: true, requireMention: false }, }, }, },}Multi-account
Section titled “Multi-account”Accounts map to zalouser profiles in OpenClaw state. Example:
{ channels: { zalouser: { enabled: true, defaultAccount: "default", accounts: { work: { enabled: true, profile: "work" }, }, }, },}Environment variables
Section titled “Environment variables”The Zalo Personal plugin can also read profile selection from environment variables:
ZALOUSER_PROFILE: profile name to use when noprofileis set in channel or account config.ZCA_PROFILE: legacy fallback profile name, used only whenZALOUSER_PROFILEis not set.
Profile names select the saved Zalo login credentials in OpenClaw state. Resolution order is:
- Explicit
profilein config. ZALOUSER_PROFILE.ZCA_PROFILE.- The account id for non-default accounts, or
defaultfor the default account.
For multi-account setups, prefer setting profile on each account in config so
one environment variable does not make multiple accounts share the same login
session.
Typing, reactions, and delivery acknowledgements
Section titled “Typing, reactions, and delivery acknowledgements”- OpenClaw sends a typing event before dispatching a reply (best-effort).
- Message reaction action
reactis supported forzalouserin channel actions.- Use
remove: trueto remove a specific reaction emoji from a message. - Reaction semantics: Reactions
- Use
- For inbound messages that include event metadata, OpenClaw sends delivered + seen acknowledgements (best-effort).
Troubleshooting
Section titled “Troubleshooting”Login doesn’t stick:
openclaw channels status --probe- Re-login:
openclaw channels logout --channel zalouser && openclaw channels login --channel zalouser
Allowlist/group name didn’t resolve:
- Use numeric IDs in
allowFrom/groupAllowFromand stable group IDs ingroups. If you intentionally need exact friend/group names, enablechannels.zalouser.dangerouslyAllowNameMatching: true.
Upgraded from old CLI-based setup:
- Remove any old external
zcaprocess assumptions. - The channel now runs fully in OpenClaw without external CLI binaries.
Related
Section titled “Related”- Channels Overview — all supported channels
- Pairing — DM authentication and pairing flow
- Groups — group chat behavior and mention gating
- Channel Routing — session routing for messages
- Security — access model and hardening