An agent-first CLI for Monday.com. Pull tasks, file backlog items, transition statuses, and post comments from the terminal — designed for AI coding agents (Claude Code, Codex, Aider) with humans as a welcome second audience.
AI coding agents need to operate on real tickets. Monday.com has a
GraphQL API, but each agent learning that schema from scratch is
wasteful — and the API is sharp-edged (40+ column types, idiosyncratic
mutation shapes, complex pagination). monday-cli is the abstraction:
one stable contract (universal envelope, 29 stable error codes,
JSON Schema introspection) that every agent can target.
- Agent-first ergonomics.
--jsoneverywhere, stableerror.code, deterministicmeta, no interactive prompts. monday board describeemits paste-ready--set <token>=<value>examples for every writable column — agents discover board structure without reading external docs.monday schema --jsondumps every command's input flags and output shape as JSON Schema 2020-12 — no--helpscraping.--dry-runon every mutation;confirmation_requiredfor destructive bulk ops (no surprise deletes).- Two-layer token redaction scrubs the API token from every emitted byte (logs, error messages, stack traces). Hardened against an adversarial fixture suite.
npm install -g monday-cliRequires Node.js ≥ 22.
# 1. Set your Monday API token (admin or member; guests can't mint one).
# Get one at https://<your-org>.monday.com/admin/integrations/api
export MONDAY_API_TOKEN="<your-token>"
# 2. Smoke test
monday account whoami --json
# 3. List a board's items (replace 12345 with your board ID)
monday item list --board 12345 --json
# 4. File a new task (v0.2)
monday item create --board 12345 --name "Refactor login" \
--set status=Backlog --set 'Due date'=+1w --json
# 5. Find-or-create with idempotent matching (v0.2)
# Re-running with the same args is safe — 0/1/2+ matches route to
# create / update / `ambiguous_match` (the 27th stable error code).
monday item upsert --board 12345 --name "Refactor login" \
--match-by name --set status='Working on it' --json
# 6. Move a ticket forward
monday item set 67890 status=Done --json
# 7. Comment on it
monday update create 67890 --body "Shipped in PR #1234" --jsonThe CLI follows a monday <noun> <verb> shape:
# Discovery
monday account whoami
monday board list
monday board describe <board-id> # full board schema with column types
# Reading items
monday item list --board <board-id>
monday item list --board <board-id> --where status=Backlog --where owner=me
monday item list --board <board-id> --all --output ndjson | jq '...'
monday item get <item-id>
monday item find "Refactor login" --board <board-id>
monday item search --board <board-id> --where status=Done
monday item subitems <item-id>
# Updating items
monday item set <item-id> status=Done
monday item update <item-id> --set status=Done --set 'Due date'=+1w
monday item clear <item-id> status
# Comments (Monday "updates")
monday update list <item-id>
monday update create <item-id> --body "Shipped in PR #1234"
# Schemas (the agent's discovery hammer)
monday schema # full registry as JSON Schema 2020-12
monday schema item.set # one command's schema (dotted name)
# Diagnostics + escape hatch
monday board doctor <board-id> # flag duplicate titles, non-writable
# column types, broken board_relations
monday raw '{ me { id name email } }' # GraphQL escape hatch
monday raw 'mutation { ... }' --allow-mutation --dry-runFor worked agent walkthroughs (pick up a backlog item → mark
in-progress → leave a comment → mark done), filter DSL syntax,
dry-run shapes, and error handling, see
docs/examples.md.
- TTY (you in a terminal): human-friendly tables, truncated to fit the terminal width.
- Pipe / redirect: JSON, no flags needed —
monday item list | jqworks. - Agent in a pseudo-TTY: pass
--json(alias for--output json) to force JSON regardless of terminal detection. JSON output is never truncated.
Every JSON response uses the same universal envelope:
{
"ok": true,
"data": ...,
"meta": {
"schema_version": "1",
"api_version": "2026-01",
"cli_version": "0.2.0",
"request_id": "0e6f1a7b-...",
"source": "live",
"cache_age_seconds": null,
"retrieved_at": "2026-05-01T10:00:00Z",
"complexity": null
},
"warnings": []
}Errors carry a stable error.code — agents key off the code,
never the English message:
{
"ok": false,
"error": {
"code": "rate_limited",
"message": "...",
"retryable": true,
"retry_after_seconds": 30,
"details": { "...": "..." }
},
"meta": { "..." }
}The full envelope and error-code contract live in
docs/cli-design.md §6 (binding) and
docs/output-shapes.md (per-command
reference).
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Usage error (bad args, confirmation_required) |
| 2 | API or network error |
| 3 | Config error (missing token, etc.) |
| 130 | SIGINT (Ctrl-C) |
If you're an AI coding agent driving this CLI:
- Always pass
--json. Pseudo-TTY detection isn't reliable inside an agent harness.--jsonis an alias for--output jsonand forces JSON on every command. JSON is never truncated; tables are. - Branch on
error.code, noterror.message. The 29 stable codes (not_found,confirmation_required,column_archived,unsupported_column_type,rate_limited,stale_cursor,ambiguous_match,tag_not_found,oauth_failed, …) are part of the contract. Messages are not. - Read
meta.sourceto know whether the data is"live"/"cache"/"mixed"/"none"."mixed"means board metadata came from cache while the rest hit live — non-trivial for writes because Monday's column state may have drifted.cache_age_secondstells you how stale the cached portion is. - Discover commands via
monday schema --json. Every command's input flags + outputdatashape are introspectable as JSON Schema 2020-12 — no--helpscraping. - Discover board structure via
monday board describe <board-id> --json. Each writable column carriesexample_set, paste-ready--set <token>=<value>strings the agent can use without external Monday docs. - Use
--dry-runon any mutation to preview the change as aplanned_changes[]envelope before committing. Bulk ops without--yesreturnconfirmation_required(exit 1) by default. - Per-command output reference lives in
docs/output-shapes.md— whatdatalooks like for every shipped command. Worked agent sessions indocs/examples.md.
The CLI reads configuration from environment variables. Source priority (first match wins):
MONDAY_API_TOKENinprocess.env(current shell).MONDAY_API_TOKEN=...in a.envfile in the working directory.
--token <value> is not a supported flag — tokens passed on the
command line leak via ps, shell history, and crash dumps. If you
must pass one inline, prefer MONDAY_API_TOKEN=... monday ... so
the token stays in the process env only.
The CLI sends Authorization: <token> (no Bearer prefix).
Monday's API rejects the Bearer form.
See .env.example for all supported variables
(API URL override, API-Version pin, request timeout, etc.).
v0.2.0 (published — first npm release as monday-cli):
the v0.1 read-only core + safe-mutations surface PLUS the full
mutation surface (item lifecycle, update mutations, workspace
lifecycle, board lifecycle, board columns + groups). Built
incrementally across M8–M18; one breaking change vs v0.1 (see
CHANGELOG.md for the full upgrade guide).
npm install -g monday-cli pulls the published artifact.
v0.1.0 (git tag, foundation milestone — not published to npm
under the monday-cli name): read-only core (account, workspace,
board, user, update, item) + safe mutations (item set /
item clear / item update single + bulk, update create) +
diagnostics (board doctor) + GraphQL escape hatch (raw) +
filter DSL (--where + --filter-json) + cursor pagination with
stale-cursor fail-fast + NDJSON streaming + local cache. v0.1.0
shipped to main as the foundation milestone but the npm publish
slipped to v0.2.0; the v0.1 surface is fully present in the
published v0.2.0 tarball.
What v0.2 added:
- M8 added the
--set-raw <col>=<json>escape hatch (bypasses the friendly translator; gated against read-only-forever and files-shaped types) and thelink/email/phonefirm-row friendly translators. - M9 added
monday item create— top-level + classic-only subitem creation with single round-trip semantics, optional positional placement (--position before|after --relative-to <iid>), and the same--set/--set-rawsurface asitem update. - M10 closed the item-lifecycle cluster —
monday item archive/delete/duplicate. The two destructive verbs share the--yesconfirmation gate (--dry-runexempts) and read the source item for the dry-run preview;archiveis wire-level idempotent,deletenon-idempotent (re-running after an interimcreatewould target the new item).duplicateis creative (no--yes), runs two-leg live (board lookup + mutation — Monday requiresboard_id), takes--with-updatesto copy the source's comments, and extends the live envelope'sdatawithduplicated_from_idso an agent has the source-ID echo handy. - M11 closed the four-verb lifecycle set with
monday item move— same-board (--to-group <gid>) viamove_item_to_groupor cross-board (--to-group <gid> --to-board <bid>) viamove_item_to_board. Cross-board moves use--columns-mapping '{<src>: <target>}'to bridge columns whose IDs differ between source and target; the strict default rejects unmatched columns pre-mutation withdetails.unmatched+details.example_mapping(agents copy-paste the seed into their retry) rather than letting Monday silently drop them.--columns-mapping {}is the explicit "drop everything (Monday's permissive default)" opt-in.--to-groupis required for both forms because Monday'smove_item_to_board(group_id: ID!)is mandatory. Value-overrides on cross-board mappings deferred to v0.3 (Monday'sColumnMappingInputcarries no value slot — agents firemonday item setpost-move when they need overrides). - M12 ships the idempotency cluster —
monday item upsert- bulk
monday item clear --where. Upsert takes--match-by <col>[,<col>...]plus--name/--setand routes 0 / 1 / 2+ matches tocreate_item/update_item/ambiguous_match(the 27th stable error code). Sequential- retry idempotent — re-running with the same args from the same agent is safe; concurrent agents are NOT a uniqueness guarantee (agents should pick a stable hidden key column for--match-byso race-induced duplicates surface asambiguous_matchon the next call). The match-by safe-list is intentionally narrow in v0.2:name/text/long_text/numbers/ external_id-shaped hidden text round-trip verbatim;status/dropdownround-trip via label-text;peopleis restricted tome;date/link/email/phoneare NOT v0.2-safe (the lookup-leg vs mutation-leg grammars don't reconverge at the wire — see cli-design §5.8 for the per-kind breakdown). Bulkclear --whereextends M5b's per-item clear with the same cursor walk +--yesgate + per-item failure decoration as bulkupdate --where.
- bulk
Writer allowlist (other types return unsupported_column_type
with per-category guidance):
status, text, long_text, numbers, dropdown, date,
people, plus M8 firm row link, email, phone, plus v0.3-M19
row tags, board_relation, dependency.
-
M13 ships the full update mutation surface —
monday update reply/edit/delete/like/unlike/pin/unpin/clear-all. The eight new verbs introduce the partial- success envelope (update clear-allreturnsok: truewith per-target outcomes indata.results: [...]); the envelope isok: truewhenever the dispatch ran, with per-target failures surfacing asdata.results[i].error: { code, message }rather than top-levelok: false. M13 also flipsupdate list's default-replies behaviour (now empty unless--with-repliesis set — the one breaking change in v0.2). -
M14 ships the workspace lifecycle —
monday workspace create/update/delete/add-users/remove-users.add-users/remove-usersreuse M13's partial-success envelope and add resolver-fronted dispatch (mixed numeric IDs- emails through
userByEmail); per-token resolution failures land as records insidedata.resultswith the input token echoed verbatim.
- emails through
-
M15 ships the board lifecycle —
monday board create/update/archive/delete/duplicate/add-users.board duplicateintroduces the wrapped envelope shape (data: { board: <projection>, is_async }) because Monday'sBoardDuplicationcarries anis_asyncslot the projection doesn't model.board updateis per-attribute fan-out across Monday'supdate_board(board_attribute, new_value)surface with a force-live final read leg (Monday's per-attribute calls return only the changed slice). -
M16 ships board column lifecycle + the §8 eager- invalidation contract —
monday board column-create/column-update/column-delete. Every board-structure mutation callsinvalidateBoard(boardId)post-success so a same-processboard describesees fresh state without TTL eviction.column-createadds thenoncanonical_column_typewarning for non-allowlisted column types with per-categorysuggested_write_path(raw_writable / read_only_forever / files_shaped). M16 retrofittedboard update/archive/deleteto participate in the §8 contract. -
M17 ships board group lifecycle —
monday board group- create/group-update/group-archive/group-duplicate/group-delete. Group-update is per-attribute fan-out across Monday's singleupdate_group(group_attribute, new_value)surface with NO force-live read leg (Monday'supdate_groupreturns the full Group projection post-mutation, distinguishing group-update from board-update). Group-create + group-update validate--coloragainst the pinned Monday-supported palette insrc/api/group-color.ts. Group-archive carries a snapshot- bearing dry-run from cached board metadata; group-delete is destructive-no-read minimal. -
M18 closed v0.2 with NDJSON streaming for
item search+update list(the missing pair vs M7'sitem liststreaming pin), envelope-snapshots refresh (60 → 92),output-shapes.mdaudit, README quickstart withitem create+item upsertexamples, this CHANGELOG, and the version bump to0.2.0.
v0.3 in progress. M19 closed — tags, board_relation,
dependency friendly translators (slipped from v0.2 tentative at
M18 close per cli-design §13 + §5.3) graduated; monday account tags read verb shipped (closes the §6.5 tag_not_found.details .hint forward-reference). M20 closed (documentation-only)
— monday item time-track start <iid> / ... stop <iid> are
registered for forward-compatibility but reject today with
usage_error: an empirical probe (2026-05-10, against API version
2026-01) confirmed Monday's public API does not currently
support time-tracking column writes (change_simple_column_value
change_column_valueboth reject; the mutation root has zero time-tracking-related mutations). When Monday ships API support, the runtime swap is one-sided insrc/api/time-tracking.ts— agent scripts targeting the verbs are stable across the swap. v0.3-M21 shipped (Part 1:5c1f7ac+81eec03+a4cb5b0; Part 2:1cd660a+3b5cb30+e21c166; close-docs086be70+ coverage push7058754):monday auth login --profile <name>+monday auth logout --profile <name>runtime bodies, the~/.monday-cli/credentialsmode-0600cache, the~/.monday-cli/config.tomlTOML loader, the--profile <name>global flag's resolution wiring throughcli/program.ts's preAction hook, and the redaction-runtime extension that folds credentials-cache tokens into the secret-bag for the §7.4.3 emission-path leak test. Pre-publish blocker (v0.3.0 release prep, after M28): swap theOAUTH_CLIENT_ID/OAUTH_CLIENT_SECRETplaceholders insrc/api/oauth.tswith the registered Monday OAuth app's values (one-time external step at https://developer.monday.com/apps with redirect URI exactlyhttp://127.0.0.1:9876/callback). v0.3-M22 shipped (3a1b465, preceded by84c6d2bredact narrowing):monday statusruns the 7-probe matrix (DNS / TCP / TLS / auth / cache writability / redaction self-test / env-var pickup) per cli-design §11.5;monday usagereports the daily Monday API operation budget remaining fromplatform_api.daily_*. The empirical-probe finding pivoted the design at pre-flight — Monday tracks operations-per-day underplatform_api.daily_*, NOT complexity points under a non-existentaccount.complexity— additive-only envelope per cli-design §11.5.3 Decision 8 closure. v0.3-M23 shipped (1f09a25implementation, preceded by1fefdb1pre-flight contract diff +9b93f15Codex round-1 fixes +fa27b60Codex round-2 fixes +3a2f1dbDecision 5 close): cross-boardmonday item search+monday board favoritesruntime bodies live. Two new src/api modules (cross-board-search.tsper-board fan-out walker,board-favorites.ts2-stage favorites resolver), one new command (monday board favorites), and an extension tomonday item searchfor the v0.3 cross-board path (--workspace <wid>/--favorites/--max-boards <n>flags with mutual-exclusion). Decision 5 pinned--max-boards 25default + hard cap 100, calibrated against wall-clock fan-out latency (NOT complexity-budget — empirical probe at 2026-05-11 measured ~25-30 complexity points per board against a ~999_950 per-call budget; latency at ~0.5-1.5s/N is the real constraint).board favoritesis a 2-stage filter + hydrate against the polymorphic top-levelQuery.favorites: [GraphqlHierarchyObjectItem!]surface (Board | Folder | Dashboard | Workspace); the verb filters client-side to Board-typed entries + hydrates viaboards(ids:)for the final row shape (id,name,state,workspace_id,url,position). Three load-bearing warnings on the cross-board path (inaccessible_boards,column_not_found_on_board,cross_board_truncated— v0.3 is single-call-only; no resumable cross-board cursor) + one on favorites (board_favorites_stale). Deferred to later v0.3 milestones:item history(M24),item update --continue-on-errorpartial-success (M25),monday devworkflow shortcuts (M26),notification send+webhook list/ create/delete(M27), multi-level subitem creation (M28). v0.4:monday item watch,--concurrency, asset uploads. Seedocs/cli-design.md§13 for the full roadmap,docs/v0.3-plan.mdfor the in-progress v0.3 milestones, anddocs/v0.2-plan.mdfor the v0.2 milestone history.
See CHANGELOG.md for the per-release contract.
docs/cli-design.md— canonical CLI contract. Start here if you want to understand the full surface, the JSON envelope, error codes, or the v0.1 vs v0.2 split.docs/output-shapes.md— per-command output reference with concrete examples.docs/examples.md— worked agent sessions.docs/architecture.md— module boundaries (commands → api → SDK).docs/api-reference.md— Monday concepts cheat sheet.docs/development.md— local dev workflow, adding a new command.CLAUDE.md— agent-facing project context and conventions.
git clone https://github.com/Firer/monday-cli.git
cd monday-cli
npm install # `prepare` hook auto-builds dist/
npm run dev -- account whoami --json # tsx-based dev runner
# Quality gates (all must pass before merge):
npm run typecheck
npm run lint
npm testThe full dev workflow + how to add a new command is in
docs/development.md. Conventions:
- Strictest TypeScript (
exactOptionalPropertyTypes,noUncheckedIndexedAccess,verbatimModuleSyntax). - No
any(lint-enforced). - Parse at every boundary with zod.
- Mock at the network boundary, not internal modules.
- Branch coverage 94%+ floor.
- Atomic commits, Conventional Commits.
PRs welcome. Read docs/cli-design.md for
the contract before writing code — anything that changes the
output envelope or error codes is a major-version bump and
requires explicit doc revision.
MIT © Nick Webster