Zalo Channel Setup and Capabilities for OpenClaw Bots

Covers the experimental Zalo channel in OpenClaw, including bundled plugin install, bot token setup, and verified direct message and group chat behavior. Useful for operators configuring Zalo bots.

Read this when

  • Working on Zalo features or webhooks

Status: experimental. Both direct messages and group chats are implemented. Verified behavior on Zalo Bot Creator / Marketplace bots is shown in the Capabilities table below.

Bundled plugin

Zalo is included as a bundled plugin in current OpenClaw releases, so packaged builds require no separate install.

If you are on an older build, or a custom install that leaves Zalo out, install the npm package yourself:

  • Install: openclaw plugins install @openclaw/zalo
  • Pinned version: openclaw plugins install @openclaw/zalo@<version> (pin only when you need reproducible installs)
  • From a local checkout: openclaw plugins install ./path/to/local/zalo-plugin
  • Details: Plugins

Quick setup

  1. Get a bot token from https://bot.zaloplatforms.com (sign in, create a bot, configure settings). The token looks like numeric_id:secret. On Marketplace bots, the runtime token you can actually use may show up in the bot's welcome message.
  2. Provide the token, either through env ZALO_BOT_TOKEN=... (default account only) or in config.
  3. Verify openclaw channels status --probe; if the Gateway is offline, start it. Config edits take effect via hot reload. Restart the Gateway when you have changed the service environment, so the new values load.
  4. On the first DM contact, approve the pairing code (the default DM policy is pairing).

Minimal config:

{
  channels: {
    zalo: {
      enabled: true,
      accounts: {
        default: {
          botToken: "12345689:abc-xyz",
          dmPolicy: "pairing",
        },
      },
    },
  },
}

Multi-account: place additional entries under channels.zalo.accounts.<id>, each carrying its own botToken/name. The flat channels.zalo.botToken form, with no accounts, is a legacy single-account shorthand. For new configs, accounts.<id>.* is preferred.

What it is

Zalo is a messaging app built for Vietnam. With its Bot API, the Gateway can run a bot across both 1:1 conversations and group chats. Routing back to Zalo is deterministic. Channel selection is never left to the model.

Covered here: Zalo Bot Creator / Marketplace bots. A separate product surface, Zalo Official Account (OA) bots, may behave differently, and is outside the scope of this page.

How it works

  • Inbound messages land in the shared channel envelope, normalized, with media placeholders.
  • Replies are always routed back to the same Zalo chat. Quote-reply stays off (replyToMode is fixed off).
  • Long-polling (getUpdates) is the default. Webhook mode can be enabled with channels.zalo.webhookUrl.
  • In groups, an @mention is required to trigger the bot. Per-channel configuration of this is not possible.

Limits

LimitValue
Outbound text chunk size2000 characters (Zalo API limit)
Media size (inbound/outbound)channels.zalo.mediaMaxMb, default 5 MB
Webhook request body1 MB, 30s read timeout
Webhook rate limit120 requests / 60s per path+client IP, then HTTP 429
Webhook replay tombstones30 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: senders who are unknown receive a pairing code. Until approval, their messages are ignored. A code stays valid for 1 hour.
    • openclaw pairing list zalo
    • openclaw pairing approve zalo <CODE>
    • Details: Pairing
  • channels.zalo.allowFrom takes numeric Zalo user IDs (usernames cannot be looked up). open needs "*".

Groups

The plugin supports group chats (chatTypes: ["direct", "group"]), controlled by mention plus group policy:

  • channels.zalo.groupPolicy: open | allowlist | disabled.
  • channels.zalo.groupAllowFrom limits which sender IDs are allowed to trigger the bot in groups. When unset, it falls back to allowFrom.
  • Default resolution: with channels.zalo configured, an unset groupPolicy resolves to open. If channels.zalo is absent altogether, runtime fails closed to allowlist.
  • Reported real-world caveat: certain Marketplace-bot setups could not add the bot to a group at all. Should that happen to you, check your bot's Zalo Bot Platform settings. The constraint sits on the platform side, not in OpenClaw policy.

Long-polling vs webhook

  • Default: long-polling (a public URL is not needed).
  • Webhook mode: configure channels.zalo.webhookUrl and channels.zalo.webhookSecret.
    • The webhook URL has to be HTTPS.
    • The webhook secret has to be between 8 and 256 characters.
    • Events arrive from Zalo carrying an X-Bot-Api-Secret-Token header, which is verified using a constant-time comparison.
    • Webhook requests are served by the gateway HTTP at channels.zalo.webhookPath (by default, the path taken from the webhook URL).
    • Requests have to use Content-Type: application/json (or a +json media type).
    • OpenClaw sends HTTP 200 back only once the raw event has been durably stored. If storage fails, HTTP 500 is returned. The durable 200 holds x-openclaw-delivery-accepted: durable. Some reverse proxies may need that header to tell an OpenClaw acceptance apart from a generic 200. It is left out of authentication, validation, and storage-error responses.
    • According to the Zalo API docs, getUpdates polling and webhook cannot be used at the same time.

Supported message types

  • Text: fully supported, split into chunks of 2000 characters.
  • Media: both inbound and outbound, limited by mediaMaxMb.
  • Reactions, threads, polls, and native commands: the plugin does not support these.
  • Streaming: block-streaming capability is declared by the plugin. Unlike certain other regional channels, Zalo offers no dedicated outbound queue or merge-text tuning knobs. If this matters for your use case, check the current behavior in your own environment.

Capabilities

FeatureStatus
Direct messagesSupported
GroupsSupported (mention-gated)
Media (inbound/outbound)Supported, capped by mediaMaxMb
ReactionsNot supported
ThreadsNot supported
PollsNot supported
Native commandsNot supported
Reply-to / quoteNot used (fixed off)

Delivery targets (CLI/cron)

Pass a chat ID as the target:

openclaw message send --channel zalo --target 123456789 --message "hi"

Troubleshooting

Bot does not respond:

  • Inspect the token: openclaw channels status --probe
  • Make sure the sender is approved (pairing or allowFrom)
  • Look at the gateway logs: openclaw logs --follow

Webhook not receiving events:

  • Make sure the webhook URL is HTTPS
  • Make sure the secret is 8-256 characters long
  • Make sure the gateway HTTP endpoint can be reached on the path you configured
  • Make sure getUpdates polling is not running as well (the two are mutually exclusive)
  • A sudden burst of requests may produce HTTP 429 (120 requests / 60s per path+IP). Retry after backing off

Configuration reference

Full configuration: Configuration

SettingDescriptionDefault
channels.zalo.enabledTurn channel startup on or offtrue
channels.zalo.accounts.<id>.botTokenBot token issued by Zalo Bot Platform-
channels.zalo.accounts.<id>.tokenFileRead the token from a file (symlinks are rejected)-
channels.zalo.accounts.<id>.nameDisplay name-
channels.zalo.accounts.<id>.enabledTurn this account on or offtrue
channels.zalo.accounts.<id>.dmPolicyDM policy for this accountpairing
channels.zalo.accounts.<id>.allowFromDM allowlist (user IDs)-
channels.zalo.accounts.<id>.groupPolicyGroup policy for this accountsee Groups
channels.zalo.accounts.<id>.groupAllowFromAllowlist of group senders; falls back to allowFrom-
channels.zalo.accounts.<id>.mediaMaxMbInbound/outbound media cap (MB)5
channels.zalo.accounts.<id>.webhookUrlTurn on webhook mode (HTTPS required)-
channels.zalo.accounts.<id>.webhookSecretWebhook secret (8-256 chars)-
channels.zalo.accounts.<id>.webhookPathWebhook path on the gateway HTTP serverwebhook URL path
channels.zalo.accounts.<id>.proxyProxy URL used for API requests-
channels.zalo.accounts.<id>.responsePrefixOverride for the outbound response prefix-
channels.zalo.defaultAccountAccount used by default when several are set updefault

channels.zalo.botToken, channels.zalo.dmPolicy, and other flat top-level keys act as the legacy single-account shorthand for the settings listed above. Either form works.

Env option: ZALO_BOT_TOKEN=... resolves the token of the default account only.

1,370 words · updated Sep 18, 2026