Complete guide for setting up and using the ClickUp CLI on Ubuntu Linux.
- Node.js and npm installed
- ClickUp account with API access
jqinstalled for JSON processing:sudo apt install jq
npm install -g clickup-cliThe binary will be installed to ~/.npm-global/bin/clickup (also aliased as cu and cu-cli).
Ensure ~/.npm-global/bin is in your PATH. Check your ~/.bashrc:
export PATH="$HOME/.npm-global/bin:$PATH"If not present, add it and reload:
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrccommand -v clickup
clickup --help- Log into ClickUp
- Click your avatar (upper-right)
- Settings → Apps
- Under "API Token", click Generate/Regenerate
- Copy the token (starts with
pk_)
# Set your token
TOKEN="pk_YOUR_API_TOKEN"
# Get Team/Workspace ID
curl -s "https://api.clickup.com/api/v2/team" \
-H "Authorization: $TOKEN" | jq -r '.teams[] | "Team: \(.name) | ID: \(.id)"'
# Get Spaces (replace TEAM_ID)
curl -s "https://api.clickup.com/api/v2/team/TEAM_ID/space" \
-H "Authorization: $TOKEN" | jq -r '.spaces[] | "Space: \(.name) | ID: \(.id)"'
# Get Lists (replace SPACE_ID)
curl -s "https://api.clickup.com/api/v2/space/SPACE_ID/list" \
-H "Authorization: $TOKEN" | jq -r '.lists[] | "List: \(.name) | ID: \(.id)"'Important: The config file must be at ~/.clickup (no extension, not a directory).
cat > ~/.clickup << 'EOF'
{
"auth": "pk_YOUR_API_TOKEN_HERE",
"defaults": {
"list": "YOUR_DEFAULT_LIST_ID"
},
"users": {
"alice": "123456",
"bob": "789012"
},
"lists": {
"backlog": "list_id_1",
"sprint": "list_id_2"
}
}
EOFConfig Fields:
auth- Your ClickUp API token (required)defaults.list- Default list ID for creating tasksusers- Optional: User ID aliases (use-a aliceinstead of-a 123456)lists- Optional: List ID aliases (use-l sprintinstead of-l list_id)
# Create task in default list
clickup create "Task name"
# With description
clickup create "Task name" -d "Task description"
# With priority (1=urgent, 2=high, 3=normal, 4=low)
clickup create "Task name" -d "Description" -i 2
# In specific list
clickup create "Task name" -l 901710139876
# Using list alias (from config)
clickup create "Task name" -l sprint
# With assignees
clickup create "Task name" -a alice,bob
# With markdown description from file
clickup create "Task name" -f /path/to/description.md
# All options (using saved user ID)
clickup create "Task name" \
-d "Description" \
-i 2 \
-a $CLICKUP_USER_ID \
-l sprint \
-e 3600000
# Note: -p (points) may not work if your list doesn't have points configuredCreate Options:
-d, --description- Task description-i, --priority- Priority (1-4)-a, --assignees- Comma-separated user IDs or aliases-l, --list- List ID or alias-f, --file- Markdown description from file-e, --time_estimate- Time estimate in milliseconds-p, --points- Sprint points-t, --parent- Parent task ID (for subtasks)-j, --json- Custom fields as JSON
# Update name
clickup update TASK_ID -n "New task name"
# Update description
clickup update TASK_ID -d "New description"
# Update priority
clickup update TASK_ID -i 3
# Multiple fields
clickup update TASK_ID -n "New name" -d "New description" -i 2
# Change status (must be valid status name for the list)
clickup update TASK_ID -s "in progress"
# Add to different lists
clickup update TASK_ID -l backlog,sprintUpdate Options: Same as create options, plus:
-n, --name- New task name-s, --status- Status name (must exist in the list)
# Add comment
clickup comment TASK_ID "This is my comment"
# Add checklist item
clickup check TASK_ID "Checklist item description"
# Delete task
clickup delete TASK_IDThe CLI only supports create/update/delete. For searching and querying tasks, use the API directly:
CLICKUP_TOKEN="pk_YOUR_TOKEN"
curl -s "https://api.clickup.com/api/v2/task/TASK_ID" \
-H "Authorization: $CLICKUP_TOKEN" | jqLIST_ID="901710139876"
curl -s "https://api.clickup.com/api/v2/list/$LIST_ID/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq '.tasks[] | {id, name, status: .status.status, priority: .priority.priority}'# Get tasks with specific status
curl -s "https://api.clickup.com/api/v2/list/$LIST_ID/task?statuses[]=backlog&statuses[]=in%20progress" \
-H "Authorization: $CLICKUP_TOKEN" | jq
# Get tasks assigned to user
curl -s "https://api.clickup.com/api/v2/list/$LIST_ID/task?assignees[]=USER_ID" \
-H "Authorization: $CLICKUP_TOKEN" | jqSince the clickup CLI doesn't have a list command, add these helper functions to your ~/.bashrc for listing and searching tasks:
# ClickUp CLI Helper Functions
export CLICKUP_TOKEN="pk_YOUR_TOKEN"
export CLICKUP_LIST="YOUR_LIST_ID"
export CLICKUP_USER_ID="YOUR_USER_ID"
export CLICKUP_TEAM_ID="YOUR_WORKSPACE_ID"
# List tasks in default list
clickup-list() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.tasks[] | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"'
}
# Get task details
clickup-get() {
curl -s "https://api.clickup.com/api/v2/task/$1" \
-H "Authorization: $CLICKUP_TOKEN" | jq '{id, name, description, status: .status.status, priority: .priority.priority, assignees: [.assignees[].username]}'
}
# Search tasks by name
clickup-search() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r --arg name "$1" '.tasks[] | select(.name | contains($name)) | "\(.id) | \(.name) | \(.status.status)"'
}
# List tasks with more details (table format)
clickup-list-detailed() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.tasks[] | [.id, .name, .status.status, (.priority.priority // "none"), (.assignees[0].username // "unassigned")] | @tsv' | column -t -s $'\t'
}
# List tasks assigned to specific user (by username)
clickup-my-tasks() {
local username="${1:-raymond mintz}"
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r --arg user "$username" '.tasks[] | select(.assignees[] | .username == $user) | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"'
}
# List tasks assigned to me (by email)
clickup-assigned-to-me() {
local email="${1:-YOUR_EMAIL@example.com}"
local user_id="${CLICKUP_USER_ID:-}"
local team_id="${CLICKUP_TEAM_ID:-${CLICKUP_WORKSPACE_ID:-}}"
local include_closed="${CLICKUP_INCLUDE_CLOSED:-false}"
local subtasks="${CLICKUP_INCLUDE_SUBTASKS:-true}"
local page=0
local resp tasks count err
# If an email was provided and we have a team_id, try to resolve the user_id for that email.
if [ -n "$team_id" ] && [ -n "$email" ]; then
local resolved_id
resolved_id="$(curl -s "https://api.clickup.com/api/v2/team" \
-H "Authorization: $CLICKUP_TOKEN" | \
jq -r --arg email "$email" --arg team "$team_id" \
'.teams[] | select(.id == $team) | .members[]? | select(.user.email == $email) | .user.id' | head -n 1)"
if [ -n "$resolved_id" ]; then
user_id="$resolved_id"
fi
fi
if [ -n "$team_id" ] && [ -n "$user_id" ]; then
while :; do
resp="$(curl -s "https://api.clickup.com/api/v2/team/$team_id/task?page=$page&assignees%5B%5D=$user_id&include_closed=$include_closed&subtasks=$subtasks" \
-H "Authorization: $CLICKUP_TOKEN")"
if ! echo "$resp" | jq -e '.tasks | type == "array"' >/dev/null 2>&1; then
err="$(echo "$resp" | jq -r '.err // .error // .message // empty' 2>/dev/null)"
if [ -n "$err" ]; then
echo "ClickUp API error: $err" >&2
else
echo "ClickUp API error or non-JSON response." >&2
fi
break
fi
tasks="$(echo "$resp" | jq -r '.tasks[] | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"')"
[ -n "$tasks" ] && echo "$tasks"
count="$(echo "$resp" | jq -r '(.tasks | length)')"
[ "$count" -lt 100 ] && break
page=$((page + 1))
done
return 0
fi
if [ -z "$team_id" ] && [ -n "$user_id" ]; then
echo "CLICKUP_TEAM_ID is not set; using list-only fallback (CLICKUP_LIST)." >&2
fi
while :; do
resp="$(curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task?page=$page&include_closed=$include_closed&subtasks=$subtasks" \
-H "Authorization: $CLICKUP_TOKEN")"
if ! echo "$resp" | jq -e '.tasks | type == "array"' >/dev/null 2>&1; then
err="$(echo "$resp" | jq -r '.err // .error // .message // empty' 2>/dev/null)"
if [ -n "$err" ]; then
echo "ClickUp API error: $err" >&2
else
echo "ClickUp API error or non-JSON response." >&2
fi
break
fi
tasks="$(echo "$resp" | jq -r --arg email "$email" '.tasks[] | select(.assignees[]? | .email == $email) | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"')"
[ -n "$tasks" ] && echo "$tasks"
count="$(echo "$resp" | jq -r '(.tasks | length)')"
[ "$count" -lt 100 ] && break
page=$((page + 1))
done
}
# List tasks with all assignees
clickup-list-with-assignees() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.tasks[] | [.id, .name, .status.status, (.priority.priority // "none"), ([.assignees[].username] | join(", ") // "unassigned")] | @tsv' | column -t -s $'\t'
}
# List available ClickUp workspaces (teams)
clickup-teams() {
curl -s "https://api.clickup.com/api/v2/team" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.teams[] | "\(.id) | \(.name)"'
}
# Set CLICKUP_TEAM_ID by id (prints available teams if no id is provided)
clickup-set-team() {
local id="$1"
if [ -z "$id" ]; then
echo "Usage: clickup-set-team <workspace_id>" >&2
echo "Available workspaces:" >&2
clickup-teams >&2
return 1
fi
export CLICKUP_TEAM_ID="$id"
echo "CLICKUP_TEAM_ID set to $CLICKUP_TEAM_ID"
}Run this to add all functions to your ~/.bashrc at once:
cat >> ~/.bashrc << 'EOF'
# ClickUp CLI Helper Functions
export CLICKUP_TOKEN="pk_YOUR_API_TOKEN"
export CLICKUP_LIST="YOUR_DEFAULT_LIST_ID"
export CLICKUP_USER_ID="YOUR_USER_ID"
export CLICKUP_TEAM_ID="YOUR_WORKSPACE_ID"
clickup-list() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.tasks[] | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"'
}
clickup-get() {
curl -s "https://api.clickup.com/api/v2/task/$1" \
-H "Authorization: $CLICKUP_TOKEN" | jq '{id, name, description, status: .status.status, priority: .priority.priority, assignees: [.assignees[].username]}'
}
clickup-search() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r --arg name "$1" '.tasks[] | select(.name | contains($name)) | "\(.id) | \(.name) | \(.status.status)"'
}
clickup-list-detailed() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.tasks[] | [.id, .name, .status.status, (.priority.priority // "none"), (.assignees[0].username // "unassigned")] | @tsv' | column -t -s $'\t'
}
clickup-my-tasks() {
local username="${1:-raymond mintz}"
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r --arg user "$username" '.tasks[] | select(.assignees[] | .username == $user) | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"'
}
clickup-assigned-to-me() {
local email="${1:-YOUR_EMAIL@example.com}"
local user_id="${CLICKUP_USER_ID:-}"
local team_id="${CLICKUP_TEAM_ID:-${CLICKUP_WORKSPACE_ID:-}}"
local include_closed="${CLICKUP_INCLUDE_CLOSED:-false}"
local subtasks="${CLICKUP_INCLUDE_SUBTASKS:-true}"
local page=0
local resp tasks count err
# If an email was provided and we have a team_id, try to resolve the user_id for that email.
if [ -n "$team_id" ] && [ -n "$email" ]; then
local resolved_id
resolved_id="$(curl -s "https://api.clickup.com/api/v2/team" \
-H "Authorization: $CLICKUP_TOKEN" | \
jq -r --arg email "$email" --arg team "$team_id" \
'.teams[] | select(.id == $team) | .members[]? | select(.user.email == $email) | .user.id' | head -n 1)"
if [ -n "$resolved_id" ]; then
user_id="$resolved_id"
fi
fi
if [ -n "$team_id" ] && [ -n "$user_id" ]; then
while :; do
resp="$(curl -s "https://api.clickup.com/api/v2/team/$team_id/task?page=$page&assignees%5B%5D=$user_id&include_closed=$include_closed&subtasks=$subtasks" \
-H "Authorization: $CLICKUP_TOKEN")"
if ! echo "$resp" | jq -e '.tasks | type == "array"' >/dev/null 2>&1; then
err="$(echo "$resp" | jq -r '.err // .error // .message // empty' 2>/dev/null)"
if [ -n "$err" ]; then
echo "ClickUp API error: $err" >&2
else
echo "ClickUp API error or non-JSON response." >&2
fi
break
fi
tasks="$(echo "$resp" | jq -r '.tasks[] | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"')"
[ -n "$tasks" ] && echo "$tasks"
count="$(echo "$resp" | jq -r '(.tasks | length)')"
[ "$count" -lt 100 ] && break
page=$((page + 1))
done
return 0
fi
if [ -z "$team_id" ] && [ -n "$user_id" ]; then
echo "CLICKUP_TEAM_ID is not set; using list-only fallback (CLICKUP_LIST)." >&2
fi
while :; do
resp="$(curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task?page=$page&include_closed=$include_closed&subtasks=$subtasks" \
-H "Authorization: $CLICKUP_TOKEN")"
if ! echo "$resp" | jq -e '.tasks | type == "array"' >/dev/null 2>&1; then
err="$(echo "$resp" | jq -r '.err // .error // .message // empty' 2>/dev/null)"
if [ -n "$err" ]; then
echo "ClickUp API error: $err" >&2
else
echo "ClickUp API error or non-JSON response." >&2
fi
break
fi
tasks="$(echo "$resp" | jq -r --arg email "$email" '.tasks[] | select(.assignees[]? | .email == $email) | "\(.id) | \(.name) | Status: \(.status.status) | Priority: \(.priority.priority // "none")"')"
[ -n "$tasks" ] && echo "$tasks"
count="$(echo "$resp" | jq -r '(.tasks | length)')"
[ "$count" -lt 100 ] && break
page=$((page + 1))
done
}
clickup-list-with-assignees() {
curl -s "https://api.clickup.com/api/v2/list/$CLICKUP_LIST/task" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.tasks[] | [.id, .name, .status.status, (.priority.priority // "none"), ([.assignees[].username] | join(", ") // "unassigned")] | @tsv' | column -t -s $'\t'
}
clickup-teams() {
curl -s "https://api.clickup.com/api/v2/team" \
-H "Authorization: $CLICKUP_TOKEN" | jq -r '.teams[] | "\(.id) | \(.name)"'
}
clickup-set-team() {
local id="$1"
if [ -z "$id" ]; then
echo "Usage: clickup-set-team <workspace_id>" >&2
echo "Available workspaces:" >&2
clickup-teams >&2
return 1
fi
export CLICKUP_TEAM_ID="$id"
echo "CLICKUP_TEAM_ID set to $CLICKUP_TEAM_ID"
}
EOF
source ~/.bashrcNote: Update YOUR_EMAIL@example.com in the clickup-assigned-to-me function with your actual email.
# List workspaces and set the default workspace
clickup-teams
clickup-set-team YOUR_WORKSPACE_ID
# List all tasks (simple format)
clickup-list
# List all tasks (detailed table format)
clickup-list-detailed
# List all tasks with assignees
clickup-list-with-assignees
# Get specific task details
clickup-get 86dzk0hu1
# Search tasks by keyword
clickup-search "Apple"
clickup-search "Compliance"
# Find YOUR tasks (by email)
clickup-assigned-to-me
clickup-assigned-to-me "youremail@example.com"
# Find tasks by username
clickup-my-tasks "raymond mintz"
clickup-my-tasks "Frank Contreras"
# Combine with grep for filtering
clickup-list | grep "in progress"
clickup-list | grep "urgent"
clickup-assigned-to-me | grep "backlog"
# Count tasks
clickup-list | grep -c "backlog"
clickup-assigned-to-me | wc -l
# Pipe to less for scrolling
clickup-list-detailed | less
clickup-list-with-assignees | lessclickup-list:
86dzk0pxn | Demo task for listing | Status: backlog | Priority: high
86dzk0hu1 | Updated Test Task from CLI | Status: backlog | Priority: high
86dzgfdj7 | Apple response: Tipping / IAP | Status: in progress | Priority: normal
clickup-list-detailed:
86dzk0pxn Demo task for listing backlog high unassigned
86dzk0hu1 Updated Test Task from CLI backlog high unassigned
86dzh5e5j Set Up Ongoing Compliance and Reporting backlog normal Anneliese Roley
clickup-get 86dzk0pxn:
{
"id": "86dzk0pxn",
"name": "Demo task for listing",
"description": "Testing the list function",
"status": "backlog",
"priority": "high",
"assignees": []
}The helper functions include several ways to filter tasks by who they're assigned to:
# By email (most reliable)
clickup-assigned-to-me
clickup-assigned-to-me "rmintz@edgecast.io"
# By username
clickup-my-tasks "raymond mintz"Note: To see tasks across all lists, set CLICKUP_TEAM_ID (workspace ID). Without it, clickup-assigned-to-me only checks the default list (CLICKUP_LIST).
clickup-my-tasks "Frank Contreras"
clickup-my-tasks "Ronald Pruitt"# Table view with all assignees
clickup-list-with-assignees
# Example output:
# 86dzh5e5j Set Up Ongoing Compliance backlog normal Anneliese Roley
# 86dzeegxq Architect & Set up ClickUp in progress urgent Frank Contreras, Alex McCarthyImportant: Tasks created with clickup create are NOT automatically assigned to you. You must explicitly assign them:
# Save your user ID in ~/.bashrc (do this once)
export CLICKUP_USER_ID="95313328" # Replace with your actual user ID
# Then create and assign to yourself using the variable
clickup create "My task" -a $CLICKUP_USER_ID
# Assign existing task to yourself
clickup update TASK_ID -a $CLICKUP_USER_ID
# Assign to multiple people
clickup update TASK_ID -a $CLICKUP_USER_ID,113012558Finding Your User ID:
Method 1 - From your email:
curl -s "https://api.clickup.com/api/v2/team" \
-H "Authorization: $CLICKUP_TOKEN" | \
jq '.teams[0].members[] | select(.user.email == "your@email.com") | {user_id: .user.id, username: .user.username}'Method 2 - From an existing task:
clickup-get TASK_ID | jq '.assignees[0].id'Pro Tip: Add export CLICKUP_USER_ID="YOUR_ID" to your ~/.bashrc so you can always use $CLICKUP_USER_ID instead of remembering the number.
Ensure ~/.npm-global/bin is in your PATH and reload your shell:
source ~/.bashrcThe config must be a file at ~/.clickup, not a directory. If you created a directory by mistake:
rm -rf ~/.clickup
# Then recreate as file (see Configuration section)When updating task status, the status name must exactly match one of the statuses configured in your ClickUp list. Check available statuses in the ClickUp UI or via API.
Verify your API token is correct in ~/.clickup and starts with pk_.
Set your workspace ID so the helper can search across all lists:
export CLICKUP_TEAM_ID="YOUR_WORKSPACE_ID"400 { err: 'not a valid points selection', ECODE: 'ITEM_223' }
This error occurs when:
- The list doesn't have a "Points" custom field configured
- You're using invalid point values for the list's configuration
Solution: Remove the -p flag from your command. Sprint points require specific ClickUp configuration and may not be available for all lists.
# This may fail
clickup update TASK_ID -p 3
# Use this instead
clickup update TASK_ID -n "Task name" -d "Description" -i 2Verify everything works:
# Create test task
clickup create "Test CLI Task" -d "Testing the CLI"
# This will output the task ID, e.g., 86dzk0hu1
# Update it
clickup update 86dzk0hu1 -i 2 -d "Updated description"
# Verify via API
curl -s "https://api.clickup.com/api/v2/task/86dzk0hu1" \
-H "Authorization: $CLICKUP_TOKEN" | jq '{id, name, description, priority: .priority.priority}'
# Delete test task
clickup delete 86dzk0hu1Environment: Ubuntu Linux Node.js Version: 18.19.1 clickup-cli Version: 1.0.19