CLI daily workflow
Every orboto CLI command, grouped by task, with realistic examples - the ticket loop, sessions, bulk ops, and generic API access.
This page assumes the CLI is installed and configured.
Run orboto help any time for the full, always-current command list -
this page groups the same commands by task with realistic examples.
Every command supports the same operations as the agent skill - instructions written for one apply to the other.
Orientation
orboto whoami # your identity, roles, and permissions
orboto primer ACME # project conventions: stack, commands, gotchas
orboto rules # the workspace's binding agent rulessession-start (below) is the recommended way to begin a work session -
it bundles identity, rules, and your in-progress work into one call.
The ticket loop
Reading a ticket
orboto ticket ACME-42By default this prints a compact, human-readable card - not raw JSON - built from the same data the web UI shows:
ACME-42 [bug/high] In Progress
Title: fix(auth): session expires early
Assignees: Jane Doe
Milestone: Sprint 5
Description (preview): session TTL was configured in minutes instead of…The description preview is truncated at 200 characters. Add --full to
get the complete, raw JSON response instead - useful when you need every
field, or you're piping the output into something else with jq:
orboto ticket ACME-42 --full | jq '.description'Listing tickets
orboto list-tickets ACME --status todo --limit 20
orboto my-tickets --project ACMElist-tickets requires a project key and defaults to --limit 50; pass
--all to remove the cap entirely (it walks every page of the cursor
internally, so a very large project may take a moment). --status
filters on the workflow category (todo / in_progress / in_review /
done), not a specific per-project status name - use
query with OQL's statusName field if
you need to filter on a custom status name instead. --parent <key>
narrows to one ticket's direct children, which is the fast way to see
everything filed under an epic.
my-tickets is the same idea scoped to your own assignments; --project
is optional there - omit it to see your tickets across every project
you're a member of.
Creating a ticket
orboto create-ticket ACME "fix(auth): session expires early" \
--type bug --priority high --label backend --label auth \
--assign jane@example.com --milestone "Sprint 5" --due 2026-09-15Every flag create-ticket accepts:
| Flag | Effect |
|---|---|
--type | task / bug / story / epic. |
--priority | blocker / high / normal / low / trivial. |
--milestone | Milestone by name or key. |
--parent | Parent ticket key/UUID - files this ticket as a child. |
--label | Repeatable - pass it once per label. |
--assign | Repeatable - one or more emails to assign immediately. |
--due | ISO date (YYYY-MM-DD). |
--private | Marks the ticket private on creation. |
--description "..." | Inline description text. |
--description-stdin | Read the description from stdin instead - use this for anything long or containing characters your shell would mangle. |
--delivery-mode | implementation / docs / review / admin / epic - what kind of work this ticket represents. |
--allow-language-mismatch | Override strict language enforcement (see below). |
--allow-duplicate + --duplicate-justification "..." | Override the duplicate block (see below). |
Two guardrails can block a create outright, and both print a clear recipe for getting past them intentionally rather than a bare error:
If the workspace enforces one language and your title/description reads as a different one:
⛔ Blocked - strict ticket-language enforcement is on.
This content reads as "en" but the workspace language is "de".
Rewrite it, or re-run with --allow-language-mismatch if the language is intentional.If the content looks like an existing ticket:
⛔ Blocked - this looks like a duplicate. Existing tickets it overlaps with:
- ACME-38 (91%): session cookie expires before the configured TTL
Extend one of these instead, or re-run with --allow-duplicate --duplicate-justification "why".Both are genuinely blocking (not warnings you can ignore) - the message itself tells you the exact flag to add if the create really is intentional despite the match.
Claiming, commenting, moving, and closing
orboto claim ACME-42claim does three things in one call - assigns you to the ticket, moves
it to in_progress if it isn't already there, and starts your timer -
and prints what it actually did:
{
"key": "ACME-42",
"status": "In Progress",
"timerStarted": true
}Flags: --sole unassigns everyone else first (a genuine take-over, not
just adding yourself); --force allows claiming a ticket that's
currently done (without it, claiming a done ticket is refused outright
- reopening is a deliberate action, not a side effect);
--no-timerassigns and moves the ticket without touching the timer.
orboto comment ACME-42 "found it - session TTL was set in minutes, not seconds"
orboto move ACME-42 in_review
orboto close ACME-42 --comment "fixed in the last commit, verified by the test suite"move <key> <category> moves between the four workflow categories -
todo / in_progress / in_review / done - and, like the UI, only
succeeds if that transition is actually allowed by the project's
configured workflow; an unreachable transition is rejected, not silently
adjusted to the nearest valid one. close is move ... done plus an
optional comment posted first, so the closing note lands before the
status change in the ticket's history.
Before creating a ticket that might already exist, check explicitly:
orboto check-similar ACME "session expires early"This runs the same similarity check create-ticket runs automatically -
useful to check before you've even written a full description.
Link a dependency (the second ticket blocks the first):
orboto deps ACME-42 ACME-40Bulk operations
Apply one change to many tickets instead of looping:
orboto bulk-close ACME-10,ACME-11,ACME-12 --comment "superseded by ACME-42"orboto bulk-create ACME --from=@drafts.json --milestone "Sprint 5"drafts.json is a JSON array of draft objects (at minimum a title
per draft; the same optional fields create-ticket takes apply per
draft). On completion it prints a summary, not a per-row echo:
{
"created": ["ACME-51", "ACME-52", "ACME-53"],
"failed": 0,
"duplicateFlagged": 1
}Each failed row prints its own line to stderr (✗ draft 2 "some title": <error>)
without stopping the rest of the batch - a bad row is isolated, not
fatal to the whole run. duplicateFlagged counts drafts that matched an
existing ticket closely enough to warn about (also echoed to stderr per
row: ⚠ ACME-53 may be a duplicate - review before treating as new work)
without blocking the create - unlike create-ticket's single-item
duplicate guard, a bulk create doesn't stop for you to decide, since
there's no one there to answer the question; it flags for a later
review pass instead. The command's exit code is 1 if anything failed,
even though everything that succeeded is still created - check failed
in the JSON, don't rely on the exit code alone to know how many landed.
orboto bulk-deps --from=@blocks.txt # lines "ACME-42 ACME-40" = 42 blocked by 40Accepts either the plain-text "A B"-per-line form shown above, or a
JSON array of [a, b] pairs - useful when the pairs come from another
command's output. An edge that already exists is counted as OK, not an
error, so re-running the same file is safe.
All three bulk commands accept --from=- to read from stdin instead of
a file - handy when a previous command's output feeds the next one, for
example piping an OQL query's matched keys straight into bulk-close.
Milestones, docs, and search
orboto milestones ACME
orboto create-milestone ACME "Sprint 5"
orboto close-milestone ACME "Sprint 5"
orboto search "login timeout" --types ticket --project ACME
orboto query "assignee = currentUser() AND statusCategory != done" --limit 25
orboto get-doc ACME-D3query runs OQL directly - the same language the
in-app search palette and the MCP orboto_query tool use. Add
--syntax jql to paste JQL instead.
Sessions and work routing
Sessions are how an agent claims and hands back work with an atomic lease rather than a plain assign - read that page for the full model (what a lease guarantees, the four "nothing ready" reasons, pausing). This section is the command-by-command mechanics.
orboto session-start --project ACME # orientation digest: rules, your work, timersession-start is the recommended way to begin any work session - one
call bundles your identity, the workspace's current agent rules (or a
compact "unchanged" acknowledgment if you already have them - see
Rules delivery), your
in-progress tickets, and your timer state. It prints as readable text
(section headers, not raw JSON) so a human or an agent reading its own
output can both use it directly.
orboto work-next ACMEwork-next pulls the best available ticket in a project and reserves it
under a lease. On success it prints the full JSON reservation
(including the ticket, its context bundle, and the session id - which
the CLI also remembers locally, so work-finish afterward doesn't need
you to pass it again). Add --role review to pull from the review lane
instead of open work, and --agent-tag <tag> to prefer
agent:<tag>-labeled tickets (see
Work routing).
When there's nothing to pull, work-next exits with code 3 (never an
error code) and explains exactly why on stderr:
Nothing ready (all-leased). Candidates considered: 4. Retry in ~90s (earliest known free: 2026-08-30T14:05:00Z).or, if a pause switch is on:
Autonomous work is PAUSED for this agent (operator decision, per-bot or workspace-wide). Idle and wait for an operator instruction or an agent-notify wakeup - do not poll. Explicitly named work-start still works.That second message is a deliberate instruction, not just a status: when
paused, an autonomous loop should idle rather than keep polling -
work-start against a specific ticket still works even while paused,
since pausing only gates the self-directed pull.
orboto work-start ACME-42 --role implementerwork-start leases one specific ticket instead of pulling whatever's
best. Use it when a human directs an agent at a particular ticket rather
than letting it choose. --takeover reclaims a lease that expired
(the previous holder crashed or never finished) instead of failing;
--on-conflict queue waits for a currently-held lease to free up
instead of the default reject behavior, which fails immediately so an
automated loop can move on to something else rather than block.
orboto work-finish --commit abc1234 --status done --tests --notes "added regression test"work-finish releases the lease and records the evidence that closes
the loop. Without a session id argument, it releases whichever session
work-start/work-next most recently reserved in this checkout - pass
one explicitly only when you're finishing a session other than your most
recent one. --build / --tests / --lint are verification flags (set
the ones you actually ran - they're not assumed true), --commit records
the commit SHA, --notes is a free-text explanation, and --status
bundles a workflow move into the same call so you don't need a separate
move/close. Pass --cancel instead of --status to release the
lease without marking anything complete - use this when you're stopping
work on a ticket without finishing it (handing it back for someone else
to pick up).
orboto work-sessions --ticket ACME-42 # who (if anyone) currently holds this ticket
orboto work-sessions --mine # every session you currently hold
orboto work-sessions --mine --include-closed # ...plus your finished/cancelled historySupervising a headless coding agent
orboto pi-runner --project ACME --session-name my-agent \
--bootstrap "Run orboto session-start, then work tickets via the orboto CLI." \
-- --provider anthropic --model "*sonnet*"Runs a headless coding-agent session and turns your orboto agent inbox into live wakeups for it: message the bot's identity with a project scope, and the running session receives it as a prompt (or a mid-turn steering message) - no MCP server needed, the session talks to orboto through this same binary.
Agent inbox and time tracking
orboto messages --project ACME # your agent inbox
orboto messages --ack <id>,<id> # acknowledge messages
orboto agent-notify teammate@example.com "ready for review" --project ACME
orboto timer # current running timer, if any
orboto timer-start ACME-42
orboto timer-stopFull API access (generic verbs)
The named commands above cover the frequent operations. The entire REST API stays reachable through generic verbs, so a new endpoint never needs a new CLI subcommand before it's usable:
orboto get /users/me
orboto post /v1/agent/notify '{"targetEmail":"bot@example.com","subject":"ready"}'
echo '{"title":"A"}' | orboto post /projects/<id>/tickets -
orboto patch /tickets/<id> '{"priority":"high"}'
orboto delete /tickets/<id>/labels/<labelId>The trailing - reads the JSON body from stdin instead of an inline
argument - useful for larger payloads or piping from another command.
Discover available endpoints and their schemas via your instance's
/docs page (interactive OpenAPI reference).
Full command reference
| Group | Commands |
|---|---|
| Identity + orientation | whoami, primer <projectKey>, rules |
| Tickets | ticket, list-tickets, my-tickets, create-ticket, claim, comment, move, close, check-similar, deps, bulk-close, bulk-create, bulk-deps |
| Milestones + docs + search | milestones, create-milestone, close-milestone, search, query, get-doc |
| Sessions | session-start, work-start, work-next, work-finish, work-sessions |
| pi supervisor | pi-runner |
| Agent inbox + time | messages, agent-notify, timer, timer-start, timer-stop |
| Generic API access | get, post, patch, put, delete |
| Other | version, help, self-update |
Troubleshooting
| Symptom | Fix |
|---|---|
work-next exits with code 3 | Not an error - it means no ticket is currently ready to pull (empty backlog, or the project/workspace pause switch is on). Retry later, or check Agents administration for pause state. |
claim succeeds but no timer starts | Passed --no-timer, or a different agent instance under the same account already owns a running timer. Check orboto timer. |
| A ticket won't move to the status you expect | move/close enforce the project's configured workflow transitions, same as the UI - list the project's statuses with orboto get /projects/<id>/ticket-statuses to see what's actually reachable from the current one. |
bulk-* reports partial failures | Bulk commands isolate errors per item - a bad row doesn't abort the rest. Re-run with just the failed keys/rows after fixing the input. |
Generic verb returns 404 for a route you can see in /docs | Check the base URL includes (or excludes) /api consistently with what your instance expects, and that the path matches exactly - trailing slashes and path params are not forgiving. |