WeChat Work OpenClaw Adapter
by @richagain
Integrate WeChat Work (Enterprise WeChat) with OpenClaw for intelligent messaging. Enables receiving messages from WeChat Work, processing them with Claude A...
clawhub install wecom-openclawπ About This Skill
name: wecom-openclaw description: Integrate WeChat Work (Enterprise WeChat) with OpenClaw for intelligent messaging. Enables receiving messages from WeChat Work, processing them with Claude AI, and sending async replies. Handles msg_signature verification, AES-256-CBC encryption/decryption, and secure token-based authentication. Use when setting up bidirectional WeChat Work β OpenClaw communication.
WeChat Work β OpenClaw Adapter
Quick Start
# 1. Deploy
bash /scripts/deploy.sh2. Edit .env with your WeChat Work credentials
nano ~/wecom-adapter/.env3. Start
cd ~/wecom-adapter && npm start4. Expose publicly
cloudflared tunnel --url http://localhost:80905. Copy tunnel URL β WeChat Work admin β webhook URL
Architecture
WeChat Work β HTTPS β Cloudflare Tunnel β Node.js Adapter (8090)
ββ Verify msg_signature (SHA1)
ββ Decrypt message (AES-256-CBC)
ββ Return "success" within 5s
ββ Async: call OpenClaw β send reply via WeCom API
Key: Adapter returns success immediately, then sends AI reply asynchronously via WeChat Work's message/send API. This avoids the 5-second timeout.
β οΈ Critical Gotchas (Learned the Hard Way)
1. Parameter name is msg_signature, NOT signature
WeChat Work sends ?msg_signature=xxx, not ?signature=xxx. Reading req.query.signature will always be undefined.
2. Signature must include echostr/encrypt
GET verification: SHA1(sort([token, timestamp, nonce, echostr]))
POST messages: SHA1(sort([token, timestamp, nonce, encrypt]))
NOT SHA1(sort([token, timestamp, nonce])) β the encrypted payload MUST participate in the signature.
3. echostr must be DECRYPTED before returning
WeChat Work sends an AES-encrypted echostr. You must:
1. Verify msg_signature
2. AES-256-CBC decrypt the echostr
3. Strip PKCS#7 padding
4. Extract message from format: 16-byte random + 4-byte length (BE) + message + CorpID
5. Return the decrypted message (not the raw echostr)
4. APP_SECRET β EncodingAESKey
access_token for sending messages5. Express body parser must accept multiple Content-Types
WeChat Work may send XML as text/xml, application/xml, or other types:
app.use(express.text({ type: ['application/xml', 'text/xml', 'text/plain', '*/*'] }));
6. Async reply pattern is mandatory
WeChat Work requires response within 5 seconds. AI responses take 5-30s. Solution:
1. Return res.status(200).send('success') immediately
2. Call OpenClaw asynchronously
3. Send reply via POST /cgi-bin/message/send?access_token=xxx
7. IP whitelist required for sending messages
WeChat Work API (qyapi.weixin.qq.com) requires your server's public IP in the app's trusted IP list. Error 60020 means IP not whitelisted.
8. Enterprise verification required
β οΈ Unverified enterprises risk account suspension. WeChat may ban accounts that use API automation without proper enterprise verification. Complete verification before production use.
9. Cloudflare quick tunnels are unstable
Quick (account-less) tunnels generate new URLs on restart and may disconnect unexpectedly. For production, use Named Tunnels ($7/mo) or a static IP.
Environment Variables
CORP_ID=ww... # From WeChat Work admin
AGENT_ID=1000003 # Application agent ID
AGENT_SECRET=xxx # EncodingAESKey (43-char Base64, for encryption)
APP_SECRET=xxx # Application Secret (for access_token)
WEBHOOK_TOKEN=xxx # Token configured in webhook settings
OPENCLAW_TOKEN=xxx # OpenClaw gateway bearer token
OPENCLAW_BASE_URL=http://localhost:18789 # OpenClaw gateway URL
CLAUDE_MODEL=claude-haiku-4-5 # AI model
Files
scripts/deploy.sh β One-command deploymentscripts/index.js β Production-ready adapter (all fixes applied)references/setup-guide.md β Step-by-step WeChat Work configurationreferences/security-guide.md β Security architecture and hardeningTroubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| signature=undefined | Using req.query.signature | Use req.query.msg_signature |
| Signature mismatch | echostr/encrypt not in calculation | Include 4th element in sort array |
| -30065 error | Returning encrypted echostr | Decrypt before returning |
| bad decrypt | Wrong key or setAutoPadding(true) | Use setAutoPadding(false) + manual PKCS#7 |
| bodyιΏεΊ¦=undefined | Body parser doesn't match Content-Type | Accept */* in express.text() |
| 60020 IP error | Server IP not whitelisted | Add public IP in WeChat Work console |
| Timeout | 5s limit exceeded | Use async pattern: return success, send via API |
| Account banned | Unverified + automated messages | Verify enterprise first |
π‘ Examples
# 1. Deploy
bash /scripts/deploy.sh2. Edit .env with your WeChat Work credentials
nano ~/wecom-adapter/.env3. Start
cd ~/wecom-adapter && npm start4. Expose publicly
cloudflared tunnel --url http://localhost:80905. Copy tunnel URL β WeChat Work admin β webhook URL
π Tips & Best Practices
| Symptom | Cause | Fix |
|---------|-------|-----|
| signature=undefined | Using req.query.signature | Use req.query.msg_signature |
| Signature mismatch | echostr/encrypt not in calculation | Include 4th element in sort array |
| -30065 error | Returning encrypted echostr | Decrypt before returning |
| bad decrypt | Wrong key or setAutoPadding(true) | Use setAutoPadding(false) + manual PKCS#7 |
| bodyιΏεΊ¦=undefined | Body parser doesn't match Content-Type | Accept */* in express.text() |
| 60020 IP error | Server IP not whitelisted | Add public IP in WeChat Work console |
| Timeout | 5s limit exceeded | Use async pattern: return success, send via API |
| Account banned | Unverified + automated messages | Verify enterprise first |