Skip to content

NPLUS-Dev/Clickup-Management-Tool

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ClickUp Task CLI

A single-file program (clickup_tasks.py) that calls the ClickUp REST API v2 directly to create a task -> get its task ID -> change its status, all in one place. No external SDK — just requests. (ClickUp has no official Python SDK; its own guidance is to call the REST API directly.)

task.py adds a thin workflow layer on top: turn a command into a task in the progress status, then flip it to review when the work is done.

Workflow (task.py)

The intended loop: the assistant (Claude) turns a user command into an English task name + description, registers it in ClickUp as progress, does the work, then moves it to review.

# one-time setup
python task.py lists                          # find a list_id
python task.py config --list-id 901817138679  # remember it (this repo: "Cycle Task Lists")

# per command
python task.py start --name "Fix login redirect loop" \
                     --description "Users bounce between /login and /home after SSO; trace the redirect."
#   -> creates the task in "progress", PRINTS the task_id, and tracks it by that id
python task.py current                         # list every tracked task by id + status
python task.py review --task-id <task_id>      # when done -> moves THAT task to "review"
  • start resolves the configured start status (progress) against the list's real statuses fuzzily (so progress also matches in progress), and tracks the new task by its id in .task_state.json (shape: {"tasks": {<id>: {...}}, "order": [...]}).
  • Status is changed per task id, not via a shared "current". review / set-status require --task-id — printed by start and listed by current. This is deliberate: several starts (e.g. concurrent sessions writing the same state file) each track their own id, so a status change can only ever move the id you name. (The old behavior moved the most-recently started task, which silently moved the wrong one when another session had started a task in between.)
  • start also assigns the task to you by default (the token owner; auto-detected and cached as assignee_id). Override with --assignee <user_id>, or --no-assign to skip; set a default with config --assignee me|<id>|none.
  • review re-fetches the task, finds its list's statuses, fuzzy-matches review, and updates it.
  • set-status --task-id <id> --status <name> moves any task to any status (also fuzzy-matched).
  • Token is read from .env (or the CLICKUP_TOKEN env var); .env, .task_state.json, and .task_config.json are git-ignored.

Low-level CLI (clickup_tasks.py)

1. Install

pip install -r requirements.txt

2. Generate & configure the token

  1. Log in to ClickUp -> avatar (top right) -> Settings -> Apps (left sidebar)
  2. Under API Token, click Generate and copy the pk_... token
  3. Set it as an environment variable (a personal token goes into the header RAW, with no Bearer prefix)
# macOS / Linux
export CLICKUP_TOKEN=pk_xxxxxxxx

# Windows PowerShell
$env:CLICKUP_TOKEN = "pk_xxxxxxxx"

Only an OAuth access token uses the Bearer {token} form. A personal token is used as-is.

3. Usage

# 0) Verify authentication
python clickup_tasks.py whoami

# 1) Find the lists you can access and their list_id
python clickup_tasks.py lists

# 2) See the status names available in a list (statuses differ per list!)
python clickup_tasks.py statuses --list-id 901234567

# 3) Full demo: create -> print ID -> change status -> re-fetch to verify
python clickup_tasks.py demo --list-id 901234567

# Individual actions
python clickup_tasks.py create --list-id 901234567 --name "My task" --status "to do" --priority 3
python clickup_tasks.py set-status --task-id 86abc12 --status "in progress"
python clickup_tasks.py get --task-id 86abc12 --json

4. How it works (verified API spec)

Step Method & endpoint Notes
Create POST /list/{list_id}/task body {"name": "..."} (only name is required). The response's top-level id is the new task ID
Read valid statuses GET /list/{list_id} the response's statuses[].status are the valid status names for that list
Change status PUT /task/{task_id} body {"status": "..."}PUT (not PATCH). The name is case-insensitive
  • Base URL: https://api.clickup.com/api/v2
  • Auth header: Authorization: pk_... (raw)
  • Statuses are not a global enum — they are defined per Space/Folder/List. So before the PUT, set-status checks the name against that list's valid statuses (case-insensitive) and returns a friendly message instead of a 400 if it's wrong. (Disable with --no-validate.)

5. Rate limiting

Free / Unlimited / Business plans allow 100 req/min per token; exceeding it returns HTTP 429. The client automatically waits until the X-RateLimit-Reset (Unix seconds) header and retries. (The Retry-After header is not guaranteed by ClickUp's docs, so X-RateLimit-Reset takes priority.)

6. References

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages