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

  1. 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.
  2. Configure the token, either as env ZALO_BOT_TOKEN=... (default account only) or in config.
  3. Restart the gateway.
  4. 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 (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

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: 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 behavior: long-polling, so no public URL is needed.
  • For webhook mode, configure channels.zalo.webhookUrl and channels.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-Token header 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/json or a +json media 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 200 contains x-openclaw-delivery-accepted: durable, so reverse proxies can require it to tell apart OpenClaw acceptance from a generic 200 (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

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)

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

SettingDescriptionDefault
channels.zalo.enabledTurn channel startup on or offtrue
channels.zalo.accounts.<id>.botTokenBot token from Zalo Bot Platform-
channels.zalo.accounts.<id>.tokenFileRead token from a file (symlinks rejected)-
channels.zalo.accounts.<id>.nameDisplay name-
channels.zalo.accounts.<id>.enabledEnable or disable this accounttrue
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>.groupAllowFromGroup sender allowlist; falls back to allowFrom-
channels.zalo.accounts.<id>.mediaMaxMbMedia cap for inbound/outbound (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 for API requests-
channels.zalo.accounts.<id>.responsePrefixOutbound response prefix override-
channels.zalo.defaultAccountDefault account when multiple are configureddefault

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.

1,269 words · updated Aug 1, 2026