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
# (`monday auth login` is registered but the OAuth flow is not yet
# available — authenticate via the env var.)
export MONDAY_API_TOKEN="<your-token>"
# 2. Smoke test — confirm the token works.
monday account whoami --json
# 3. Install shell completion (bash / zsh / fish). The default mode
# emits raw script bytes on stdout (so the redirect works); `--json`
# opts into the envelope.
monday completion bash >> ~/.bashrc # or .zshrc / config.fish
# 4. Is everything wired up?
monday status --json # DNS / TCP / TLS / auth / cache probe matrix
monday usage --json # remaining daily Monday API operations
# 5. Discover a board's shape (columns / groups / views).
monday board describe 12345 --json # full schema + example_set per writable column
monday board views 12345 --json # views only (Kanban / Gantt / Calendar / Table / …)
# 6. List a board's items.
monday item list --board 12345 --json
# 7. File a new task.
monday item create --board 12345 --name "Refactor login" \
--set status=Backlog --set 'Due date'=+1w --json
# 8. Long-poll for activity on an item (NDJSON stream).
# Per-event NDJSON record + a `{"_meta": {...}}` trailer carrying
# the session counters. Use `--once` to drain backlog without
# polling further; SIGINT (Ctrl-C) drains gracefully and exits 130.
monday item watch 67890 --once # or --max-events 50 --max-duration 1h
# 9. Upload a file to a column or to an update (comment).
# Both surface `--dry-run` for an envelope preview without the
# multipart round-trip.
monday item upload 67890 --column 'Attachments' ./screenshot.png --json
monday update upload <update-id> ./diagram.png --json
# 10. Parallel partial-success bulk updates. `--concurrency <N>` (1..32)
# opts into parallel dispatch; envelope is byte-equivalent to the
# sequential `--concurrency 1` default, input order is preserved in
# `data.results[]` regardless of completion order.
monday item update --where status=Backlog --set status='Working on it' \
--board 12345 --yes --continue-on-error --concurrency 4 --json
# 11. Browse the workdocs surface.
monday doc list --workspace 5 --order-by used_at --limit 10 --json
monday doc get 88001 --json # full Document with blocks
# 12. Workdocs CRUD. Doc-level: create / rename / delete / duplicate.
monday doc create-in-workspace --workspace 5 --name "Design notes" --json
monday doc rename 88001 --name "Design notes (v2)" --json
monday doc duplicate 88001 --with-updates --json
monday doc delete 88001 --yes --json
# Per-block: block-create / block-update / block-delete.
monday doc block-create 88001 --type normal_text --content '{"text":"hi"}' --json
# Bulk import from HTML / markdown — no per-block round-trips.
monday doc import-html --workspace 5 --html ./page.html --title "Imported" --json
monday doc append-markdown 88001 --markdown ./notes.md --json
# 13. Team writers.
monday user team-list --json
monday user team-create --name "Platform" --users 7,9 --json
monday user team-add-members <tid> --users 11,13 --json
# 14. File-column friendly `--set` writes — every shape reaching
# Monday's file-upload wire. `--dry-run` emits `planned_changes` on
# any of these without the multipart round-trip.
monday item set 67890 'Attachments'=./screenshot.png --json
monday item update 67890 --set 'Attachments'=./diagram.png --json
monday item update --board 12345 --where status=Backlog \
--set 'Attachments'=./report.pdf --yes --continue-on-error \
--concurrency 4 --json # bulk file dispatch
monday item create --board 12345 --name "Field report" \
--set 'Attachments'=./report.pdf --set 'Spec'=./spec.pdf \
--set status='Working on it' --json # multi-file at create-time
cat report.pdf | monday item set 67890 'Attachments'=- \
--filename report.pdf --json # stdin file source
# 15. Find-or-create with idempotent matching. Re-running with the
# same args is safe — 0 / 1 / 2+ matches route to create / update
# / `ambiguous_match` (a stable error code agents can key off).
monday item upsert --board 12345 --name "Refactor login" \
--match-by name --set status='Working on it' --json
# 16. Move a ticket forward, then comment on it.
monday item set 67890 status=Done --json
monday update create 67890 --body "Shipped in PR #1234" --json
# 17. Monday Dev convention layer (sprint / epic / release / task).
# First-time setup auto-detects boards by Monday's stock template
# names.
monday dev discover --apply --json # writes ~/.monday-cli/config.toml
monday dev sprint current --json # the active sprint
monday dev task list --mine --json # my open tasks
# 18. Outbound writes — webhooks + notifications.
monday webhook list 12345 --json
monday notification send --user 7 --target 67890 \
--target-type item --text "PTAL" --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.8.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.12.0 (current). Adds monday config set/get/unset and a
[profiles.<name>.defaults] config block for profile-scoped argument
defaults — set a default board, workspace, output format, or
concurrency once and every command picks it up (CLI flag still wins,
then env var, then the profile default). 122 commands across
boards, items (single + bulk via --where), columns, groups,
workspaces, teams, workdocs, updates, files, and the monday dev
workflow namespace. No breaking changes vs v0.11.0.
What's next. Roadmap headlines (see
docs/cli-design.md §13 for the full
roadmap):
- Cross-board
monday item movewith column-value overrides - Resumable cross-board search cursor
A user-entity contract migration plus Monday API version pin bumps
(to 2026-04, then 2026-07) defer pending upstream Monday SDK
releases.
See CHANGELOG.md for the full per-release history.
docs/cli-design.md— canonical CLI contract. Start here if you want to understand the full surface, the JSON envelope, error codes, or the per-version scope (§13).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 95.45% floor for branches; 95% floor for
statements / functions / lines (v0.3-M22 ratcheted branches
from 94% via an OAuth coverage-push session — see
vitest.config.ts). - 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