Introduction
# ClickUp Skill
Interact with ClickUp's REST API for task management, reporting, and workflow automation.
## Configuration
Before using this skill, ensure the following are configured in `TOOLS.md`:
- **API Token:** `CLICKUP_API_KEY` - **Team/Workspace ID:** `CLICKUP_TEAM_ID` - **Space IDs** (optional, for filtering) - **List IDs** (optional, for creating tasks)
## Quick Start
### Using the Helper Script
The fastest way to query ClickUp:
```bash # Set environment variables export CLICKUP_API_KEY="pk_..." export CLICKUP_TEAM_ID="90161392624"
# Get all open tasks ./scripts/clickup-query.sh tasks
# Get task counts (parent vs subtasks) ./scripts/clickup-query.sh task-count
# Get assignee breakdown ./scripts/clickup-query.sh assignees
# Get specific task ./scripts/clickup-query.sh task <task-id> ```
### Direct API Calls
For custom queries or operations not covered by the helper script:
```bash # Get all open tasks (with subtasks and pagination) curl "https://api.clickup.com/api/v2/team/{team_id}/task?include_closed=false&subtasks=true" \ -H "Authorization: {api_key}" ```
## Critical Rules
### 1. ALWAYS Include Subtasks
**Never** query tasks without `subtasks=true`:
```bash # ✅ CORRECT ?subtasks=true
# ❌ WRONG (no subtasks parameter) ```
**Why:** Without this parameter, you miss potentially 70%+ of actual tasks. Parent tasks are just containers; real work happens in subtasks.
### 2. Handle Pagination
ClickUp API returns max 100 tasks per page. **Always** loop until `last_page: true`:
```bash page=0 while true; do result=$(curl -s "...&page=$page" -H "Authorization: $CLICKUP_API_KEY") # Process tasks echo "$result" | jq '.tasks[]' # Check if done is_last=$(echo "$result" | jq -r '.last_page') [ "$is_last" = "true" ] && break ((page++)) done ```
**Why:** Workspaces with 300+ tasks need 3-4 pages. Missing pages = incomplete data.
### 3. Distinguish Parent Tasks vs Subtasks
```bash # Parent tasks have parent=null jq '.tasks[] | select(.parent == null)'
# Subtasks have parent != null jq '.tasks[] | select(.parent != null)' ```
## Common Operations
### Get Task Counts
```bash # Using helper script (recommended) ./scripts/clickup-query.sh task-count
# Direct API with jq curl -s "https://api.clickup.com/api/v2/team/{team_id}/task?subtasks=true" \ -H "Authorization: {api_key}" | \ jq '{ total: (.tasks | length), parents: ([.tasks[] | select(.parent == null)] | length), subtasks: ([.tasks[] | select(.parent != null)] | length) }' ```
### Get Assignee Breakdown
```bash # Using helper script (recommended) ./scripts/clickup-query.sh assignees
# Direct API curl -s "https://api.clickup.com/api/v2/team/{team_id}/task?subtasks=true" \ -H "Authorization: {api_key}" | \ jq -r '.tasks[] | if .assignees and (.assignees | length) > 0 then .assignees[0].username else "Unassigned" end' | sort | uniq -c | sort -rn ```
### Create a Task
```bash curl "https://api.clickup.com/api/v2/list/{list_id}/task" \ -X POST \ -H "Authorization: {api_key}" \ -H "Content-Type: application/json" \ -d '{ "name": "Task Name", "description": "Description here", "assignees": [user_id], "status": "to do", "priority": 3 }' ```
### Update a Task
```bash curl "https://api.clickup.com/api/v2/task/{task_id}" \ -X PUT \ -H "Authorization: {api_key}" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Name", "status": "in progress", "priority": 2 }' ```
### Get Specific Task
```bash # Using helper script ./scripts/clickup-query.sh task {task_id}
# Direct API curl "https://api.clickup.com/api/v2/task/{task_id}" \ -H "Authorization: {api_key}" ```
## Advanced Queries
### Filter by Space
```bash curl "https://api.clickup.com/api/v2/team/{team_id}/task?space_ids[]={space_id}&subtasks=true" \ -H "Authorization: {api_key}" ```
### Filter by List
```bash curl "https://api.clickup.com/api/v2/list/{list_id}/task?subtasks=true" \ -H "Authorization: {api_key}" ```
### Include Closed Tasks
```bash curl "https://api.clickup.com/api/v2/team/{team_id}/task?include_closed=true&subtasks=true" \ -H "Authorization: {api_key}" ```
## Reference Documentation
For detailed API documentation, query patterns, and troubleshooting:
**Read:** `references/api-guide.md`
Covers: - Full API endpoint reference - Response structure details - Common gotchas and solutions - Rate limits and best practices - Task object schema
## Workflow Patterns
### Daily Standup Report
```bash # Get all open tasks grouped by assignee ./scripts/clickup-query.sh assignees
# Get specific team member's tasks (use user ID, not username!) curl "https://api.clickup.com/api/v2/team/{team_id}/task?subtasks=true&assignees[]={user_id}" \ -H "Authorization: {api_key}" ```
### Task Audit
```bash # Count tasks by status ./scripts/clickup-query.sh tasks | \ jq -r '.tasks[].status.status' | sort | uniq -c | sort -rn
# Find unassigned tasks ./scripts/clickup-query.sh tasks | \ jq '.tasks[] | select(.assignees | length == 0)' ```
### Priority Analysis
```bash # Count by priority ./scripts/clickup-query.sh tasks | \ jq -r '.tasks[] | .priority.priority // "none"' | sort | uniq -c | sort -rn ```
## Tips
- **Helper script first:** Use `scripts/clickup-query.sh` for common operations - **Direct API for custom:** Use curl when you need specific filters or updates - **Always read api-guide.md:** Contains full endpoint reference and troubleshooting - **Check TOOLS.md:** For workspace-specific IDs and configuration - **Test with small queries:** When unsure, test with `| head -n 5` first - **Filter by user ID:** Use `assignees[]={user_id}` parameter, not jq username matching
## Troubleshooting
- **Missing tasks?** → Add `subtasks=true` - **Only 100 tasks returned?** → Implement pagination loop - **401 Unauthorized?** → Check `CLICKUP_API_KEY` is set correctly - **Rate limit error?** → Wait 1 minute (100 requests/min limit) - **Empty assignees array?** → Task is unassigned (not an error) - **Assignee filter returns fewer tasks than expected?** → Use user ID in `assignees[]` param, not jq text matching