Intelligently schedule meetings via feishu-cli: automatically find common free time, filter meeting rooms by capacity/floor, accept/decline invitations. AI Agents mainly use this skill for scheduling meetings.
Basic Calendar CRUD(list/get/primary/create-event/list-events/update-event/delete-event/agenda/event-search/event-reply/attendee/freebusy) please refer to
feishu-cli-toolkitSection 2 "Calendar and Events". This skill focuses on the three new smart subcommands added in v1.18+.
Core Concepts
Positioning of the Trio
| Subcommand | What It Solves | When to Use |
|---|---|---|
suggestion |
Recommend common free time slots for a group of participants | First step in scheduling a meeting: set the time |
room-find |
Find available meeting rooms for a given time slot | Second step in scheduling a meeting: set the location |
rsvp |
Accept/decline/tentatively accept received invitations | Invitee handles invitations |
Typical combination: first suggestion get recommended time slots → room-find filter meeting rooms for that time slot → calendar create-event create the event and invite participants and meeting rooms.
Underlying Implementation & Retries
- Direct calls to OpenAPI
/open-apis/calendar/v4/freebusy/suggestionand.../freebusy/room_find, SDK v3.5.3 does not expose these two methods. room-findbuilt-inDoWithRetry(MaxRetries=3 / MaxTotalAttempts=8 / RetryOnRateLimit=true)——429 rate limiting does not count toward failure attempts, full-jitter backoff, cap 30s.room-findMulti-slot batching uses concurrent calls (default 10 workers), 429 is retried by each goroutine individually, no user-level backoff needed.suggestioncurrentlydoes not have DoWithRetry attached(single call), if running frequently in manual scripts please sleep 1-2s yourself.
Identity Selection
| Command | Token Behavior |
|---|---|
suggestion / room-find |
User Token preferred + App Token fallback: when already auth login use User Token (check private busy/free), if not logged in fall back to App Token (check public busy/free, company-bookable meeting rooms).--user-access-token can be explicitly specified. |
rsvp |
User Token required(reply to invitations as yourself), if not logged in an error is reported directly. |
Permissions:calendar:calendar.free_busy:read(suggestion / room-find)、calendar:calendar.event:reply(rsvp)。
calendar rsvp supports --action, also supports the official lark-cli compatible alias --rsvp-status, the two are equivalent and different values cannot be specified at the same time.
Subcommand Quick Reference
1. calendar suggestion (find common free time)
feishu-cli calendar suggestion \
--attendee-ids ou_aaa,ou_bbb,oc_groupid \
--duration 30m \
[--start 2024-01-22T09:00:00+08:00] \
[--end 2024-01-22T18:00:00+08:00] \
[--timezone Asia/Shanghai] \
[--event-rrule "FREQ=WEEKLY;COUNT=4"] \
[--exclude 2024-01-22T12:00:00+08:00~2024-01-22T13:00:00+08:00] \
[-o json]
| Parameter | Description | Default Value |
|---|---|---|
--attendee-ids(required) |
List of participant IDs, comma-separated,ou_xxx(user) + oc_xxx(group chat) mixed |
— |
--duration(required) |
Meeting duration,30m / 1h30m / 90(plain number is in minutes), range 1-1440 |
— |
--start |
Search start (RFC3339) | Current time |
--end |
Search end (RFC3339) | start 23:59:59 of the same day |
--timezone |
Time zone, e.g. Asia/Shanghai |
— |
--event-rrule |
Recurrence rule rrule string (find common free time for recurring meetings) | — |
--exclude |
Exclude time slots, multiple slots separated by commas, single slot start~end RFC3339 |
— |
-o |
Output format:json for AI parsing / empty for human viewing |
empty |
Returns: recommended time slot list + ai_action_guidance(human-readable suggestions provided by the server).
Output example (text)
推荐时段(共 3 个):
[1] 2024-01-22T09:00:00+08:00 ~ 2024-01-22T09:30:00+08:00
理由: 全员有空
[2] 2024-01-22T10:00:00+08:00 ~ 2024-01-22T10:30:00+08:00
理由: 全员有空
[3] 2024-01-22T14:00:00+08:00 ~ 2024-01-22T14:30:00+08:00
理由: 全员有空
建议: 推荐选择上午 09:00,所有人精力较好。
2. calendar room-find (find meeting rooms)
feishu-cli calendar room-find \
--slot 2024-01-22T09:00:00+08:00~2024-01-22T10:00:00+08:00 \
[--slot 2024-01-22T14:00:00+08:00~2024-01-22T15:00:00+08:00] \
[--attendee-ids ou_aaa,ou_bbb] \
[--city "北京" --building "飞书大厦" --floor F2] \
[--room-name "01,02,03"] \
[--min-capacity 6 --max-capacity 20] \
[--timezone Asia/Shanghai] \
[-o json]
| Parameter | Description | Default value |
|---|---|---|
--slot(required) |
Time slot to query start~end(RFC3339); can be passed repeatedly or multiple slots separated by commas |
— |
--attendee-ids |
Participant ID (ou_xxx/oc_xxx), used to recommend meeting rooms close to participants |
— |
--city |
City constraint | — |
--building |
Building constraint | — |
--floor |
Floor constraint (e.g., F2) |
— |
--room-name |
Meeting room name constraint, multiple separated by commas | — |
--min-capacity / --max-capacity |
Capacity range (≥0, min ≤ max) | 0 (unlimited) |
--timezone |
Time zone | — |
--event-rrule |
Recurrence rule rrule | — |
Returns: list of available meeting rooms aggregated by time slot, including room_id/room_name/capacity/reserve_until_time. For multiple slots, queries are concurrent (10 workers); if any time slot fails, the first error is returned immediately.
Output example (text)
2024-01-22T09:00:00+08:00 ~ 2024-01-22T10:00:00+08:00
[1] 飞书大厦-F2-01 (id=omm_xxx, capacity=8)
可预订至: 2024-01-22T10:00:00+08:00
[2] 飞书大厦-F2-02 (id=omm_yyy, capacity=12)
2024-01-22T14:00:00+08:00 ~ 2024-01-22T15:00:00+08:00
(无可用会议室)
3. calendar rsvp (reply to invitation)
feishu-cli calendar rsvp \
--event-id <EVENT_ID> \
--action accept | decline | tentative \
[--calendar-id <CAL_ID>] \
[--user-access-token <TOKEN>]
| Parameter | Description | Default value |
|---|---|---|
--event-id(required) |
Event ID | — |
--action(required) |
accept / decline / tentative |
— |
--calendar-id |
Calendar ID | Primary calendar (automatically calls calendar primary) |
--user-access-token |
User Token, replies as yourself | Required (requireUserToken), errors out directly if not logged in |
and calendar event-reply difference:
| Dimension | rsvp(new) |
event-reply(old) |
|---|---|---|
| Parameter style | All flags (--event-id/--action) |
Positional parameter <calendar_id> <event_id> + --status |
| calendar-id | Can be omitted (defaults to primary calendar) | Required |
| Applicable | AI Agent scheduling | Humans typing commands directly |
Typical workflow
Workflow A: AI Agent schedules a meeting (end-to-end)
# 1. 找共同空闲(30 分钟,明早 9-12 点)
feishu-cli calendar suggestion \
--attendee-ids ou_alice,ou_bob,ou_carol \
--duration 30m \
--start 2024-01-22T09:00:00+08:00 \
--end 2024-01-22T12:00:00+08:00 \
-o json | jq '.suggestions[0]'
# 假设拿到 09:30-10:00
# 2. 在该时段找会议室(6-12 人,F2 楼)
feishu-cli calendar room-find \
--slot 2024-01-22T09:30:00+08:00~2024-01-22T10:00:00+08:00 \
--floor F2 --min-capacity 6 --max-capacity 12 \
-o json | jq '.time_slots[0].meeting_rooms[0]'
# 假设拿到 room_id=omm_xxx
# 3. 创建日程并邀请参与人 + 会议室(走 toolkit)
feishu-cli calendar create-event \
--calendar-id <主日历> \
--summary "三方对齐" \
--start 2024-01-22T09:30:00+08:00 \
--end 2024-01-22T10:00:00+08:00
# 后续 attendee add 把人和 room 加进去
Workflow B: Batch reply to invitations
# 列出待答复的邀请(走 toolkit calendar agenda + jq 过滤 status=needs_action)
feishu-cli calendar agenda --start-date 2024-01-22 --end-date 2024-01-23 -o json \
| jq -r '.events[] | select(.status=="needs_action") | "\(.event_id)"' \
| while read eid; do
feishu-cli calendar rsvp --event-id "$eid" --action accept --user-access-token <TOKEN>
done
Key flag quick reference
| Scenario | Key flag | Notes |
|---|---|---|
| Time slot format | start~end(RFC3339) |
--slot and --exclude both use ~ as separator,not -- |
| Multiple time slots | --slot a~b --slot c~d or --slot a~b,c~d |
Both StringSlice syntaxes are supported |
| Two ways to write duration | --duration 30m or --duration 30 |
Pure number = minutes; range 1-1440 |
| AI parsing | -o json |
Both suggestion / room-find support it; rsvp only has text output |
| Don't know calendar-id | rsvp omits --calendar-id |
Automatically uses primary calendar |
Pitfalls (must read)
1. ID prefix must be ou_ / oc_
--attendee-ids internally goes through SplitAttendeeIDs(raw) splits by prefix:
ou_xxx→attendee_user_idsoc_xxx→attendee_chat_ids- Other prefixes (
omm_/room_/app_meeting room or resource token) → stderr logs warn thenskip, does not block batch - Duplicate IDs are automatically deduplicated
So: copied from Feishu user_id(plain string without prefix)/union_id(on_xxx)/email will all be skipped if fed directly; you must first use feishu-cli user get or contact:user.base:readonly API to convert to ou_xxx。
2. Frequent 429s are the norm; rely on DoWithRetry
freebusy/room_find has a high 429 hit rate in batch concurrency scenarios (measured: 10 workers running 6 slots often triggers 2-3 times). The CLI has built-in RetryOnRateLimit=true——do not stack retry at the user layer, it will hit the MaxTotalAttempts=8 limit and fail early. If running large batches requires more aggressive concurrency, adjust the roomFindWorkers constant and recompile a version.
suggestion has no retry attached yet; before calling in a loop, do your own sleep 1。
3. duration unit pitfall
--duration 30= 30 minutes(not seconds, not hours)--duration 30m= 30 minutes--duration 1h30m= 90 minutes--duration 1.5h= error(time.ParseDuration does not accept decimal hours, write1h30m)- range 1-1440; anything over 24 hours errors out directly
4. start/end default values are easy to trip on
--startdefaults = current time (not 00:00 of the day)--enddefaults =start23:59:59 of the same day (note it is the same day; crossing days requires explicitly passing--end)- To find "all day tomorrow" → you must pass both
5. rsvp makes an extra API call when there is no calendar-id
Omitting --calendar-id will first call calendar primary to get the primary calendar and then call reply, adding one extra RTT. In batch reply scripts it is recommended to first feishu-cli calendar primary -o json cache the ID and then pass it to each rsvp。
6. rsvp action has only three enums
accept / decline / tentative, any other string directly 400. Common typos:accpet / declined / maybe。
7. exclude / slot time direction
start~end in end must bestrictly later than start, equality also errors. Crossing days requires the correct timezone; do not directly pass T00:00:00Z followed by +08:00 mixed together, inconsistent timezones will parse and compare chaotically.
When to switch to other skills
| Requirement | switch to |
|---|---|
| create/modify/delete schedules, list schedule agenda, event-search | feishu-cli-toolkit Section 2 "Calendar and Schedules" |
| Plain freebusy query for a single person/single time slot (no smart recommendation) | feishu-cli-toolkit Section 2 "Free/Busy Query" |
| add/remove attendee, query attendee list | feishu-cli-toolkit Section 2 "Attendee Management" |
| event-reply old API (positional-argument version) | feishu-cli-toolkit Section 2 |
| send meeting reminder messages to attendees | feishu-cli-msg + feishu-cli-card |
get ou_xxx open_id (email/user_id → open_id conversion) |
feishu-cli-toolkit Contacts section / lark-cli contact +search-user |
Permission Quick Reference
| Command | scope | Token Recommendation |
|---|---|---|
suggestion |
calendar:calendar.free_busy:read |
User Token preferred + App Token fallback |
room-find |
calendar:calendar.free_busy:read |
User Token preferred + App Token fallback |
rsvp |
calendar:calendar.event:reply |
User Token required(reply as yourself) |
Precheck:
feishu-cli auth check --scope "calendar:calendar.free_busy:read calendar:calendar.event:reply"