Zalo Channel Setup and Capabilities for OpenClaw Bots
This page covers Zalo bot support status, capabilities, and configuration for OpenClaw. It includes setup steps, token configuration, and multi-account options for developers.
Read this when
- Working on Zalo features or webhooks
Status: experimental. Both direct messages and group chats are implemented; the Capabilities table below reflects verified behavior on Zalo Bot Creator / Marketplace bots.
Bundled plugin
Current OpenClaw releases bundle Zalo as a plugin, so packaged builds skip a separate install step.
For older builds or custom installs without 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
- Generate a bot token at https://bot.zaloplatforms.com (log in, create a bot, adjust settings). The token is
numeric_id:secret; for Marketplace bots, the usable runtime token might show up in the bot's welcome message. - Configure the token, either as env
ZALO_BOT_TOKEN=...(default account only) or in config. - Restart the gateway.
- On first DM contact, approve the pairing code (default DM policy is pairing).
Minimal config:
{
channels: {
zalo: {
enabled: true,
accounts: {
default: {
botToken: "12345689:abc-xyz",
dmPolicy: "pairing",
},
},
},
},
}
Multi-account: add more entries under 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 messaging app focused on Vietnam. 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 picks 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 replay tombstones | 30 days, up to 20,000 completed events per account (keyed by 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 behavior: long-polling, so no public URL is needed.
- For webhook mode, configure
channels.zalo.webhookUrlandchannels.zalo.webhookSecret.- The webhook URL must be HTTPS.
- The webhook secret needs to be between 8 and 256 characters.
- Zalo includes an
X-Bot-Api-Secret-Tokenheader in its events, which gets verified using a constant-time comparison. - The gateway HTTP server accepts webhook requests at
channels.zalo.webhookPath, which defaults to the path from the webhook URL. - Requests have to use
Content-Type: application/jsonor a+jsonmedia type. - A 200 HTTP status is sent back only after the raw event has been stored durably; if storage fails, the response is HTTP 500. The durable
200containsx-openclaw-delivery-accepted: durable, so reverse proxies can require it to tell apart OpenClaw acceptance from a generic200(authentication, validation, and storage-error responses leave it out). - According to Zalo API docs, getUpdates polling and webhook mode cannot run at the same time.
Supported message types
- Text: fully supported, split into chunks of 2000 characters.
- Media: works both inbound and outbound, limited by
mediaMaxMb. - Reactions, threads, polls, and native commands: the plugin does not handle these.
- Streaming: the plugin advertises block-streaming capability, but Zalo lacks a dedicated outbound queue or merge-text adjustment options (unlike some other regional channels); if this matters for your scenario, test the current behavior in your setup.
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)
Point the target at a chat ID:
openclaw message send --channel zalo --target 123456789 --message "hi"
Troubleshooting
The bot stays silent:
- Double-check the token:
openclaw channels status --probe - Make sure the sender is approved (via pairing or
allowFrom) - Look at the gateway logs:
openclaw logs --follow
Webhook misses events:
- Verify that the webhook URL is HTTPS
- Verify that the secret falls within 8-256 characters
- Verify that the gateway HTTP endpoint is reachable at the configured path
- Verify that getUpdates polling is off (the two are mutually exclusive)
- A flood of requests may trigger HTTP 429 (120 requests / 60s per path+IP); apply backoff and retry
Configuration reference
Complete configuration: Configuration
| Setting | Description | Default |
|---|---|---|
channels.zalo.enabled | Turn channel startup on or off | 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 or disable this account | true |
channels.zalo.accounts.<id>.dmPolicy | DM policy for this account | pairing |
channels.zalo.accounts.<id>.allowFrom | DM allowlist (user IDs) | - |
channels.zalo.accounts.<id>.groupPolicy | Group policy for this account | see Groups |
channels.zalo.accounts.<id>.groupAllowFrom | Group sender allowlist; falls back to allowFrom | - |
channels.zalo.accounts.<id>.mediaMaxMb | Media cap for inbound/outbound (MB) | 5 |
channels.zalo.accounts.<id>.webhookUrl | Turn on 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 older single-account shorthand for the fields listed above; both formats work.
Env option: ZALO_BOT_TOKEN=... only resolves the token for the default account.
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