Files
openclaw/docs/channels/zalo.md
Peter Steinberger f7d7148cf0 docs: rewrite published docs grounded in current source (#100142)
Source-grounded rewrite of 529 published docs pages with per-unit information-loss verification: 1,713 factual corrections cited to src/**, generated surfaces regenerated, frontmatter titles preserved for i18n, release notes pages untouched. All docs gates green.

Closes #100141
2026-07-05 00:32:47 -04:00

180 lines
9.7 KiB
Markdown

---
summary: "Zalo bot support status, capabilities, and configuration"
read_when:
- Working on Zalo features or webhooks
title: "Zalo"
---
Status: experimental. Direct messages and group chats are both implemented; the [Capabilities](#capabilities) table below reflects verified behavior on Zalo Bot Creator / Marketplace bots.
## 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](/tools/plugin)
## Quick setup
1. Create a bot token at [https://bot.zaloplatforms.com](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.
2. Set the token, either as env `ZALO_BOT_TOKEN=...` (default account only) or in config.
3. Restart the gateway.
4. Approve the pairing code on first DM contact (default DM policy is pairing).
Minimal config:
```json5
{
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 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 (`replyToMode` is fixed off).
- Long-polling (`getUpdates`) by default; webhook mode available via `channels.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 zalo`
- `openclaw pairing approve zalo <CODE>`
- Details: [Pairing](/channels/pairing)
- `channels.zalo.allowFrom` accepts numeric Zalo user IDs (no username lookup). `open` requires `"*"`.
### 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.groupAllowFrom` restricts which sender IDs can trigger the bot in groups; falls back to `allowFrom` when unset.
- Default resolution: when `channels.zalo` is configured, an unset `groupPolicy` resolves to `open`. When `channels.zalo` is missing entirely, runtime fails closed to `allowlist`.
- 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.webhookUrl` and `channels.zalo.webhookSecret`.
- Webhook URL must use HTTPS.
- Webhook secret must be 8-256 characters.
- Zalo sends events with an `X-Bot-Api-Secret-Token` header, 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 `+json` media 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:
```bash
openclaw message send --channel zalo --target 123456789 --message "hi"
```
## 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`
**Webhook not receiving events:**
- 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](/gateway/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](#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](/channels) - all supported channels
- [Pairing](/channels/pairing) - DM authentication and pairing flow
- [Groups](/channels/groups) - group chat behavior and mention gating
- [Channel Routing](/channels/channel-routing) - session routing for messages
- [Security](/gateway/security) - access model and hardening