Lark Toolkit
by @pengxiao-wang
Comprehensive Lark/Feishu API skill for OpenClaw agents. Covers all Lark operations via three access paths: claw-lark plugin (message tool), MCP tools (mcpor...
clawhub install lark-toolkitπ About This Skill
name: lark-toolkit description: "Comprehensive Lark/Feishu API skill for OpenClaw agents. Covers all Lark operations via three access paths: claw-lark plugin (message tool), MCP tools (mcporter), and direct Open API (curl). Use when: (1) sending/receiving Lark messages, (2) managing groups or members, (3) listing department users or contacts, (4) creating calendar events, (5) working with docs/bitable/wiki/OKR/tasks, (6) setting up a new Lark bot, (7) debugging webhook/connection issues, (8) any Lark Open API operation the message tool or MCP doesn't support. Covers both Lark International (open.larksuite.com) and Feishu China (open.feishu.cn)."
Lark Toolkit
Prerequisites & Security
This skill is a documentation-only reference guide. It contains no executable code that accesses credentials automatically.
Required credentials (user-provided, never bundled):
app_id) β from Lark Developer Consoleapp_secret) β from the same consoleHow credentials are used:
, , CHAT_ID, etc.) β no real secretsscripts/get_token.sh helper obtains a temporary tenant_access_token from Lark's auth API. It reads credentials from (in order):LARK_APP_ID / LARK_APP_SECRET environment variables
3. ~/.openclaw/openclaw.json (standard OpenClaw config, path channels.lark.accounts.default.appId/appSecret)
LARK_TOKEN env var for subsequent commands in the same shell sessionThree Access Paths
| Need | Path | When | |------|------|------| | Send/receive messages | claw-lark plugin (message tool) | Basic text, media, reactions β simplest | | Structured CRUD ops | MCP tools via mcporter | Bitable, calendar, docs, tasks, OKR β 38 tools | | Everything else | Direct API (curl) | Contacts, member mgmt, anything MCP doesn't cover |
Rule: claw-lark first β MCP second β direct API as fallback.
Authentication (Direct API)
TOKEN=$(curl -s -X POST 'https://open.larksuite.com/open-apis/auth/v3/tenant_access_token/internal' \
-H 'Content-Type: application/json' \
-d '{"app_id":"","app_secret":""}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['tenant_access_token'])")
Or use the helper: bash scripts/get_token.sh
Token validity: ~2 hours. Cache it.
API Base URLs
| Platform | API Base | Dev Console |
|----------|----------|-------------|
| Lark International | https://open.larksuite.com/open-apis/ | https://open.larksuite.com/app |
| Feishu (China) | https://open.feishu.cn/open-apis/ | https://open.feishu.cn/app |
β οΈ Lark β Feishu. Always confirm which platform the tenant uses.
Common API Patterns
Send a Message
curl -X POST "https://open.larksuite.com/open-apis/im/v1/messages?receive_id_type=chat_id" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"receive_id":"CHAT_ID","msg_type":"text","content":"{\"text\":\"hello\"}"}'
Reply in Thread
curl -X POST "https://open.larksuite.com/open-apis/im/v1/messages/MSG_ID/reply" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"msg_type":"text","content":"{\"text\":\"reply\"}","reply_in_thread":true}'
Add Reaction
curl -X POST "https://open.larksuite.com/open-apis/im/v1/messages/MSG_ID/reactions" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"reaction_type":{"emoji_type":"THUMBSUP"}}'
Emoji types: THUMBSUP HEART LAUGH OK COOL FINGERHEART SMILE JIAYOU
List Department Users (MCP gap β direct API only)
# List root departments
curl -s -H "Authorization: Bearer $TOKEN" \
'https://open.larksuite.com/open-apis/contact/v3/departments?parent_department_id=0&page_size=50&fetch_child=true'List users in a department
curl -s -H "Authorization: Bearer $TOKEN" \
'https://open.larksuite.com/open-apis/contact/v3/users?department_id=&page_size=50'
Key fields: name, open_id, employee_type (1=regular, 2=intern), department_ids
Read Chat History
curl -s -H "Authorization: Bearer $TOKEN" \
'https://open.larksuite.com/open-apis/im/v1/messages?container_id_type=chat&container_id=&page_size=20&sort_type=ByCreateTimeDesc'
Add Bot to Group
curl -X POST "https://open.larksuite.com/open-apis/im/v1/chats//members?member_id_type=app_id" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"id_list":[""]}'
MCP Tools (38 available)
mcporter call lark-mcp. key=value
Full catalog with parameters: references/mcp-tools.md
MCP Coverage
| Module | Key Tools | |--------|-----------| | Bitable | create apps/tables, CRUD records, list fields | | Calendar | create/get/patch events, free/busy, primary calendar | | Docs | read content, search, import, set permissions | | IM | create/list groups, get members, send messages, list history | | OKR | batch get, list periods, CRUD progress, query reviews | | Report | query rules/tasks, manage views | | Task | create/patch tasks, add members/reminders | | Wiki | search nodes, get node details | | Contacts | batch get user IDs by email/phone |
MCP Gaps (use direct API)
GET /contact/v3/users?department_id=GET /contact/v3/departmentsPOST /im/v1/chats/{chat_id}/membersPOST /im/v1/messages/{msg_id}/reactionsPOST /im/v1/images / POST /im/v1/filesPagination
Most list APIs use cursor-based pagination:
?page_size=50&page_token=
Check has_more in response.
Error Handling
| Code | Meaning | |------|---------| | 0 | Success | | 99991663 | Token expired β refresh | | 99991664 | Token invalid | | 99991400 | Bad request | | 99991403 | No permission β check app permissions |
Critical Pitfalls
1. Lark β Feishu β International uses open.larksuite.com, China uses open.feishu.cn
2. open_id is per-app β Same user has different open_id across different Lark apps
3. Webhook 5s timeout β Return 200 immediately, process async
4. Event dedup β Use event_id (Lark retries up to 3x)
5. Bot-to-bot blind spot β Lark does NOT push Bot A's messages to Bot B's webhook
6. Publishing required β Permission/event changes only take effect after publishing a new app version
7. ngrok IPv6 trap β Use 127.0.0.1:PORT not localhost:PORT in ngrok config
8. ngrok free domain β Returns interstitial HTML that Lark rejects. Use paid domain.