Bundled plugin
Zalo ships as a bundled plugin in current OpenClaw releases, so packaged builds do not need a separate install. On an older build or a custom install that excludes Zalo, install the npm package directly:- Install:
openclaw plugins install @openclaw/zalo - Pinned version:
openclaw plugins install @openclaw/zalo@2026.6.11 - From a local checkout:
openclaw plugins install ./path/to/local/zalo-plugin - Details: Plugins
Quick setup
- Create a bot token at https://bot.zaloplatforms.com (sign in, create a bot, configure settings). The token is
numeric_id:secret; for Marketplace bots the usable runtime token may appear in the bot’s welcome message. - Set the token, either as env
ZALO_BOT_TOKEN=...(default account only) or in config. - Restart the gateway.
- Approve the pairing code on first DM contact (default DM policy is pairing).
channels.zalo.accounts.<id>, each with its own botToken/name. channels.zalo.botToken (flat, no accounts) is a legacy single-account shorthand; prefer accounts.<id>.* for new configs.
What it is
Zalo is a Vietnam-focused messaging app. Its Bot API lets the Gateway run a bot for both 1:1 conversations and group chats, with deterministic routing back to Zalo (the model never chooses channels). This page covers Zalo Bot Creator / Marketplace bots. Zalo Official Account (OA) bots are a different product surface and may behave differently; this page does not cover them.How it works
- Inbound messages are normalized into the shared channel envelope with media placeholders.
- Replies always route back to the same Zalo chat; quote-reply is not used (
replyToModeis fixed off). - Long-polling (
getUpdates) by default; webhook mode available viachannels.zalo.webhookUrl. - Groups require an @mention to trigger the bot; this is not configurable per channel.
Limits
| Limit | Value |
|---|---|
| Outbound text chunk size | 2000 characters (Zalo API limit) |
| Media size (inbound/outbound) | channels.zalo.mediaMaxMb, default 5 MB |
| Webhook request body | 1 MB, 30s read timeout |
| Webhook rate limit | 120 requests / 60s per path+client IP, then HTTP 429 |
| Webhook duplicate-event window | 5 minutes (keyed on path + account + event name + chat + sender + message id) |
Access control
Direct messages
channels.zalo.dmPolicy:pairing(default) |allowlist|open|disabled.- Pairing: unknown senders get a pairing code; messages are ignored until approved. Codes expire after 1 hour.
openclaw pairing list zaloopenclaw pairing approve zalo <CODE>- Details: Pairing
channels.zalo.allowFromaccepts numeric Zalo user IDs (no username lookup).openrequires"*".
Groups
Group chats are supported by the plugin (chatTypes: ["direct", "group"]) and gated by mention plus group policy:
channels.zalo.groupPolicy:open|allowlist|disabled.channels.zalo.groupAllowFromrestricts which sender IDs can trigger the bot in groups; falls back toallowFromwhen unset.- Default resolution: when
channels.zalois configured, an unsetgroupPolicyresolves toopen. Whenchannels.zalois missing entirely, runtime fails closed toallowlist. - Reported real-world caveat: on some Marketplace-bot setups the bot could not be added to a group at all. If you hit that, verify with your bot’s Zalo Bot Platform settings; it is a platform-side constraint, not an OpenClaw policy.
Long-polling vs webhook
- Default: long-polling (no public URL required).
- Webhook mode: set
channels.zalo.webhookUrlandchannels.zalo.webhookSecret.- Webhook URL must use HTTPS.
- Webhook secret must be 8-256 characters.
- Zalo sends events with an
X-Bot-Api-Secret-Tokenheader, checked with a constant-time comparison. - Gateway HTTP handles webhook requests at
channels.zalo.webhookPath(defaults to the webhook URL’s path). - Requests must use
Content-Type: application/json(or a+jsonmedia type). - getUpdates polling and webhook are mutually exclusive per Zalo API docs.
Supported message types
- Text: full support, chunked to 2000 characters.
- Media: inbound/outbound, capped by
mediaMaxMb. - Reactions, threads, polls, native commands: not supported by the plugin.
- Streaming: the plugin declares block-streaming capability, but Zalo has no dedicated outbound queue/merge-text tuning knobs (unlike some other regional channels); verify current behavior in your environment if this matters for your use case.
Capabilities
| Feature | Status |
|---|---|
| Direct messages | Supported |
| Groups | Supported (mention-gated) |
| Media (inbound/outbound) | Supported, capped by mediaMaxMb |
| Reactions | Not supported |
| Threads | Not supported |
| Polls | Not supported |
| Native commands | Not supported |
| Reply-to / quote | Not used (fixed off) |
Delivery targets (CLI/cron)
Use a chat ID as the target:Troubleshooting
Bot does not respond:- Check the token:
openclaw channels status --probe - Verify the sender is approved (pairing or
allowFrom) - Check gateway logs:
openclaw logs --follow
- Confirm the webhook URL uses HTTPS
- Confirm the secret is 8-256 characters
- Confirm the gateway HTTP endpoint is reachable on the configured path
- Confirm getUpdates polling is not also running (they are mutually exclusive)
- A burst of requests can return HTTP 429 (120 requests / 60s per path+IP); back off and retry
Configuration reference
Full configuration: Configuration| Setting | Description | Default |
|---|---|---|
channels.zalo.enabled | Enable/disable channel startup | true |
channels.zalo.accounts.<id>.botToken | Bot token from Zalo Bot Platform | - |
channels.zalo.accounts.<id>.tokenFile | Read token from a file (symlinks rejected) | - |
channels.zalo.accounts.<id>.name | Display name | - |
channels.zalo.accounts.<id>.enabled | Enable/disable this account | true |
channels.zalo.accounts.<id>.dmPolicy | Per-account DM policy | pairing |
channels.zalo.accounts.<id>.allowFrom | DM allowlist (user IDs) | - |
channels.zalo.accounts.<id>.groupPolicy | Per-account group policy | see Groups |
channels.zalo.accounts.<id>.groupAllowFrom | Group sender allowlist; falls back to allowFrom | - |
channels.zalo.accounts.<id>.mediaMaxMb | Inbound/outbound media cap (MB) | 5 |
channels.zalo.accounts.<id>.webhookUrl | Enable webhook mode (HTTPS required) | - |
channels.zalo.accounts.<id>.webhookSecret | Webhook secret (8-256 chars) | - |
channels.zalo.accounts.<id>.webhookPath | Webhook path on the gateway HTTP server | webhook URL path |
channels.zalo.accounts.<id>.proxy | Proxy URL for API requests | - |
channels.zalo.accounts.<id>.responsePrefix | Outbound response prefix override | - |
channels.zalo.defaultAccount | Default account when multiple are configured | default |
channels.zalo.botToken, channels.zalo.dmPolicy, and other flat top-level keys are the legacy single-account shorthand for the fields above; both forms are supported.
Env option: ZALO_BOT_TOKEN=... resolves the default account’s token only.
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