website/docs/user-guide/messaging/wecom.md
Connect Hermes to WeCom (企业微信), Tencent's enterprise messaging platform. The adapter uses WeCom's AI Bot WebSocket gateway for real-time bidirectional communication — no public endpoint or webhook needed.
See also: WeCom Callback for inbound webhook setup.
aiohttp and httpxhermes gateway setup
Select WeCom and scan the QR code with your WeCom mobile app. Hermes will automatically create a bot application with the correct permissions and save the credentials.
The setup wizard will:
If scan-to-create is not available, the wizard falls back to manual input:
hermes gateway setup, select WeCom, and enter the credentials when prompted:::warning Keep the Bot Secret private. Anyone with it can impersonate your bot. :::
hermes gateway setup
Select WeCom and follow the prompts. The wizard will guide you through:
Add the following to ~/.hermes/.env:
WECOM_BOT_ID=your-bot-id
WECOM_SECRET=your-secret
# Optional: restrict access
WECOM_ALLOWED_USERS=user_id_1,user_id_2
# Optional: home channel for cron/notifications
WECOM_HOME_CHANNEL=chat_id
hermes gateway
:::note Streaming and typing indicators The WeCom adapter delivers each response as a single complete message — it does not stream responses token-by-token, and it does not show a typing indicator. "Reply correlation" (below) only threads a response to its inbound request; it is not live streaming. :::
Set these in config.yaml under platforms.wecom.extra:
| Key | Default | Description |
|---|---|---|
bot_id | — | WeCom AI Bot ID (required) |
secret | — | WeCom AI Bot Secret (required) |
websocket_url | wss://openws.work.weixin.qq.com | WebSocket gateway URL |
dm_policy | open | DM access: open, allowlist, disabled, pairing |
group_policy | open | Group access: open, allowlist, disabled |
allow_from | [] | User IDs allowed for DMs (when dm_policy=allowlist) |
group_allow_from | [] | Group IDs allowed (when group_policy=allowlist) |
groups | {} | Per-group configuration (see below) |
Controls who can send direct messages to the bot:
| Value | Behavior |
|---|---|
open | Anyone can DM the bot (default) |
allowlist | Only user IDs in allow_from can DM |
disabled | All DMs are ignored |
pairing | Pairing mode (for initial setup) |
WECOM_DM_POLICY=allowlist
Controls which groups the bot responds in:
| Value | Behavior |
|---|---|
open | Bot responds in all groups (default) |
allowlist | Bot only responds in group IDs listed in group_allow_from |
disabled | All group messages are ignored |
WECOM_GROUP_POLICY=allowlist
For fine-grained control, you can restrict which users are allowed to interact with the bot within specific groups. This is configured in config.yaml:
platforms:
wecom:
enabled: true
extra:
bot_id: "your-bot-id"
secret: "your-secret"
group_policy: "allowlist"
group_allow_from:
- "group_id_1"
- "group_id_2"
groups:
group_id_1:
allow_from:
- "user_alice"
- "user_bob"
group_id_2:
allow_from:
- "user_charlie"
"*":
allow_from:
- "user_admin"
How it works:
group_policy and group_allow_from controls determine whether a group is allowed at all.groups.<group_id>.allow_from list (if present) further restricts which senders within that group can interact with the bot."*" group entry serves as a default for groups not explicitly listed.* wildcard to allow all users, and entries are case-insensitive.wecom:user: or wecom:group: prefix format — the prefix is stripped automatically.If no allow_from is configured for a group, all users in that group are allowed (assuming the group itself passes the top-level policy check).
The adapter receives media attachments from users and caches them locally for agent processing:
| Type | How it's handled |
|---|---|
| Images | Downloaded and cached locally. Supports both URL-based and base64-encoded images. |
| Files | Downloaded and cached. Filename is preserved from the original message. |
| Voice | Voice message text transcription is extracted if available. |
| Mixed messages | WeCom mixed-type messages (text + images) are parsed and all components extracted. |
Quoted messages: Media from quoted (replied-to) messages is also extracted, so the agent has context about what the user is replying to.
WeCom encrypts some inbound media attachments with AES-256-CBC. The adapter handles this automatically:
aeskey field, the adapter downloads the encrypted bytes and decrypts them using AES-256-CBC with PKCS#7 padding.aeskey field (must be exactly 32 bytes).cryptography Python package (pip install cryptography).No configuration is needed — decryption happens transparently when encrypted media is received.
| Method | What it sends | Size limit |
|---|---|---|
send | Markdown text messages | 4000 chars |
send_image / send_image_file | Native image messages | 10 MB |
send_document | File attachments | 20 MB |
send_voice | Voice messages (AMR format only for native voice) | 2 MB |
send_video | Video messages | 10 MB |
Chunked upload: Files are uploaded in 512 KB chunks through a three-step protocol (init → chunks → finish). The adapter handles this automatically.
Automatic downgrade: When media exceeds the native type's size limit but is under the absolute 20 MB file limit, it is automatically sent as a generic file attachment instead:
Files exceeding the absolute 20 MB limit are rejected with an informational message sent to the chat.
When the bot receives a message via the WeCom callback, the adapter remembers the inbound request ID. If a response is sent while the request context is still active, the adapter uses WeCom's reply-mode (aibot_respond_msg) to correlate the response directly to the inbound message. This provides a more natural conversation experience in the WeCom client.
The full response is delivered as a single message — the adapter does not stream tokens incrementally. If the inbound request context has expired or is unavailable, the adapter falls back to proactive message sending via aibot_send_msg.
Reply-mode also works for media: uploaded media can be sent as a reply to the originating message.
The adapter maintains a persistent WebSocket connection to WeCom's gateway at wss://openws.work.weixin.qq.com.
aibot_subscribe authentication frame with the bot_id and secret.On connection loss, the adapter uses exponential backoff to reconnect:
| Attempt | Delay |
|---|---|
| 1st retry | 2 seconds |
| 2nd retry | 5 seconds |
| 3rd retry | 10 seconds |
| 4th retry | 30 seconds |
| 5th+ retry | 60 seconds |
After each successful reconnection, the backoff counter resets to zero. All pending request futures are failed on disconnect so callers don't hang indefinitely.
Inbound messages are deduplicated using message IDs with a 5-minute window and a maximum cache of 1000 entries. This prevents double-processing of messages during reconnection or network hiccups.
| Variable | Required | Default | Description |
|---|---|---|---|
WECOM_BOT_ID | ✅ | — | WeCom AI Bot ID |
WECOM_SECRET | ✅ | — | WeCom AI Bot Secret |
WECOM_ALLOWED_USERS | — | (empty) | Comma-separated user IDs for the gateway-level allowlist |
WECOM_HOME_CHANNEL | — | — | Chat ID for cron/notification output |
WECOM_WEBSOCKET_URL | — | wss://openws.work.weixin.qq.com | WebSocket gateway URL |
WECOM_DM_POLICY | — | open | DM access policy |
WECOM_GROUP_POLICY | — | open | Group access policy |
| Problem | Fix |
|---|---|
WECOM_BOT_ID and WECOM_SECRET are required | Set both env vars or configure in setup wizard |
WeCom startup failed: aiohttp not installed | Install aiohttp: pip install aiohttp |
WeCom startup failed: httpx not installed | Install httpx: pip install httpx |
invalid secret (errcode=40013) | Verify the secret matches your bot's credentials |
Timed out waiting for subscribe acknowledgement | Check network connectivity to openws.work.weixin.qq.com |
| Bot doesn't respond in groups | Check group_policy setting and ensure the group ID is in group_allow_from |
| Bot ignores certain users in a group | Check per-group allow_from lists in the groups config section |
| Media decryption fails | Install cryptography: pip install cryptography |
cryptography is required for WeCom media decryption | The inbound media is AES-encrypted. Install: pip install cryptography |
| Voice messages sent as files | WeCom only supports AMR format for native voice. Other formats are auto-downgraded to file. |
File too large error | WeCom has a 20 MB absolute limit on all file uploads. Compress or split the file. |
| Images sent as files | Images > 10 MB exceed the native image limit and are auto-downgraded to file attachments. |
Timeout sending message to WeCom | The WebSocket may have disconnected. Check logs for reconnection messages. |
WeCom websocket closed during authentication | Network issue or incorrect credentials. Verify bot_id and secret. |