orbotodocs
API & CLI

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 rules

session-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-42

By 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 ACME

list-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-15

Every flag create-ticket accepts:

FlagEffect
--typetask / bug / story / epic.
--priorityblocker / high / normal / low / trivial.
--milestoneMilestone by name or key.
--parentParent ticket key/UUID - files this ticket as a child.
--labelRepeatable - pass it once per label.
--assignRepeatable - one or more emails to assign immediately.
--dueISO date (YYYY-MM-DD).
--privateMarks the ticket private on creation.
--description "..."Inline description text.
--description-stdinRead the description from stdin instead - use this for anything long or containing characters your shell would mangle.
--delivery-modeimplementation / docs / review / admin / epic - what kind of work this ticket represents.
--allow-language-mismatchOverride 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-42

claim 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-timer assigns 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-40

Bulk 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 40

Accepts 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.

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-D3

query 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, timer

session-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 ACME

work-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 implementer

work-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 history

Supervising 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-stop

Full 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

GroupCommands
Identity + orientationwhoami, primer <projectKey>, rules
Ticketsticket, list-tickets, my-tickets, create-ticket, claim, comment, move, close, check-similar, deps, bulk-close, bulk-create, bulk-deps
Milestones + docs + searchmilestones, create-milestone, close-milestone, search, query, get-doc
Sessionssession-start, work-start, work-next, work-finish, work-sessions
pi supervisorpi-runner
Agent inbox + timemessages, agent-notify, timer, timer-start, timer-stop
Generic API accessget, post, patch, put, delete
Otherversion, help, self-update

Troubleshooting

SymptomFix
work-next exits with code 3Not 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 startsPassed --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 expectmove/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 failuresBulk 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 /docsCheck 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.

On this page