Skip to main content
Status: experimental. Direct messages and group chats are both implemented. The 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@<version> (pin only for reproducible installs)
  • From a local checkout: openclaw plugins install ./path/to/local/zalo-plugin
  • Details: Plugins

Quick setup

  1. 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.
  2. Set the token, either as env ZALO_BOT_TOKEN=... (default account only) or in config.
  3. Check openclaw channels status --probe; start the Gateway if it is offline. Config changes follow hot reload. If you changed the service environment, restart the Gateway to load it.
  4. Approve the pairing code on first DM contact (default DM policy is pairing).
Minimal config:
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. Routing back to Zalo is deterministic. 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

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.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).
    • OpenClaw returns HTTP 200 only after it durably stores the raw event. Storage failures return HTTP 500. The durable 200 carries x-openclaw-delivery-accepted: durable. Reverse proxies can require that header to distinguish OpenClaw acceptance from a generic 200. Authentication, validation, and storage-error responses omit it.
    • 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.
  • Photo captions: truncated to fit the 2000-character limit, including polling replies.
  • Reactions, threads, polls, native commands: not supported by the plugin.
  • Streaming: the plugin declares block-streaming capability. 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

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
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 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.