Headless Automation Sessions
botmux session lets scripts, CI jobs, or scheduled jobs start an AI session without a Feishu group: send a prompt, wait for it to finish, and read the result. When people need to see it, one command publishes the session to a new group or binds it to an existing group / topic, and the conversation continues in Feishu.
Good fits:
- In CI, let the AI review code first, and only open a group to notify people when something is wrong;
- Run batch analysis from a script when you only want the text result and no chat noise;
- Do the investigation in the background, then hand the team "the existing context + a summary" in one go.
How the two command sets relate:
botmux sessionis the stable, automation-facing entry point;botmux headlessis the layer underneath. Prefersessionin scripts. Useheadlesswhen you need finer control (for example, post one turn's result without moving the session).
Three steps
Prerequisite: the botmux daemon is running on this machine (botmux start).
Step 3 does three things:
- Creates a new Feishu group with this bot (with
--chat-idit uses an existing group instead); - Opens a topic in the group and moves the session into it;
- Asks the AI to summarize the context so far (what was done, key findings and evidence, open questions, suggested next steps), then posts the summary in the topic.
From then on, replying in that topic talks to the same session with the same memory.
Session IDs
Starting a session gives you two IDs; commands accept either:
botmux headless subcommands also accept an ID prefix (it must match uniquely); session publish needs the full ID.
session commands at a glance
Every command supports --json, which prints JSON that is easy to parse with jq. Without it you get compact text:
start: the first line is the session ID, followed by the reply;send --wait/wait/result: the reply when finished, otherwise the state (such asrunning);publish: the first line is the group ID, the second is thetriggerIdof the summary turn.
send / wait / result / list / bind (and create / run) are passed straight through to the matching botmux headless subcommand with the same arguments.
Common options
--timeoutonly controls how long the command waits. When it runs out, the command exits normally while the AI keeps working; fetch the result later withsession waitorsession result. With--json, a timed-out result includes"timedOut": trueand the state is stillrunning.
Result states (state):
Exit codes: non-zero when the state is failed or not_found; 2 for invalid arguments.
When
session startfails (non-zero exit), it prints the raw JSON from the underlyingheadless run, not the compact format above; the session ID is increated.headlessId.
Publish and bind: hand it to people
Notes:
- The bot must be in the target group, or the post fails. With
--create-group, the bot creates the group itself and invites the first user in that bot'sallowedUsers. - A group made by
--create-groupis bound to the session's working directory, so new topics in that group start there too. - To change what the summary asks for,
publishaccepts--summary-prompt "<text>"or--summary-prompt-file <path>to replace the default summary prompt. - The session must be idle to be moved. If the AI is in the middle of a turn, the move is refused; try again after the turn ends.
- What moves is the same CLI process and the same memory — the same mechanism as Session Relay.
bindre-posts the latest reply into the group by default. In that case the latest turn must be finished and have a reply, or the bind is refused; add--replay noneto skip the re-post.
botmux headless: the lower layer
Use it directly when you need finer control.
bind options:
session start is equivalent to headless run --wait --json, with the output reshaped into a shorter JSON (sessionId, triggerId, state, output; the full raw response is in raw).
session invoke: model-only structured calls
This is a different mode: no agent loop, no tools at all — the model just returns a structured result that matches your JSON Schema. Good for "ask once, get JSON" tasks such as classification or extraction.
Example req.json:
Key points:
--botmust be the larkAppId here.- The requirements are strict: the bot must be a dedicated Core-only bot without sandbox, extra env vars, and so on; only some CLIs support it so far (Claude Code, Codex, OpenCode, Pi, and a few others). The
supportedfield fromcapabilitiesis the source of truth. deadlineMs(100–300000) actually stops inference when reached;--wait-ms(up to 300000) only controls how long the command waits.- The schema supports a small set of keywords (
typepropertiesrequiredadditionalPropertiesitemsenumdescription); objects must list every property inrequiredand setadditionalProperties: false. - Output is always one line of JSON. Exit code is 0 when the state is
runningorcompleted, otherwise 1.
FAQ
I get no online botmux daemon or daemon not online for bot ....
The daemon for that bot isn't running. Run botmux start, then check with botmux status.
I get multiple daemons online; pass --bot.
Several bots run on this machine; pick one with --bot on start.
Does the session post anything in Feishu? No. Until it is published or bound, the session has no group: no cards, no messages, results are only available through the commands. These turns are also outside what the Turn Results API collects.
How does this relate to HTTP triggers?
Under the hood, botmux headless sends the local daemon a task request with source.type set to headless. To call the HTTP API directly, see Trigger botmux Tasks via API.
publish failed. Is the session still there?
Yes. publish runs "create group → bind → summarize → post the summary" in order and stops with an error at the step that fails. If binding already succeeded, the session is already in the group's topic; just continue there.
Related
- Session Relay: move sessions between groups, topics, and DMs
- Trigger botmux Tasks via API: start tasks and fetch results over HTTP
- Core-only (apiOnly) API Control: botmux with only the HTTP API, no Feishu
- CLI Commands
