ClickUp Integration Playbook — Solanasis

Last updated: 2026-03-24 Architecture: Hybrid — MCP Connector (primary) + ClickUp API v2 (gap-filler) Related: AI-Native Time Logging Playbook


Table of Contents

  1. Architecture Overview
  2. MCP Connector — Complete Tool Reference
  3. MCP Connector — Gaps & Limitations
  4. ClickUp API v2 — Complete Reference
  5. ClickUp API v2 — Endpoint Deep Dive
  6. Python Integration Patterns
  7. Webhooks & Automation
  8. Troubleshooting & Maintenance

1. Architecture Overview

Integration Paths

PRIMARY PATH (daily operations):
┌─────────────┐    ┌───────────────────┐    ┌──────────┐
│ Claude Code  │───>│  MCP Connector    │───>│ ClickUp  │
│ (tool calls) │    │  (claude.ai)      │    │ API      │
└─────────────┘    └───────────────────┘    └──────────┘

FALLBACK PATH (when MCP can't do it):
┌─────────────┐    ┌───────────────────┐    ┌──────────┐
│ Claude Code  │───>│  Python Script    │───>│ ClickUp  │
│ (Bash tool)  │    │  (requests lib)   │    │ API v2   │
└─────────────┘    └───────────────────┘    └──────────┘

When to Use Which Path

ScenarioUse
Create/update/delete tasksMCP
Search for tasks, docs, anythingMCP
Track time (start/stop/manual)MCP
Read/write commentsMCP
Create/update docs and pagesMCP
Navigate workspace hierarchyMCP
Manage tags, dependencies, linksMCP
Send chat messagesMCP
Set custom field valuesMCP
Goals / OKR managementAPI script
Task checklists (sub-items)API script
Create/manage templatesAPI script
Create/manage saved viewsAPI script
Set up webhooksAPI script
Manage guests/permissionsAPI script
Manage user groupsAPI script
Bulk reporting/analyticsAPI script

Current Setup Status

The ClickUp MCP connector is connected via Claude.ai’s built-in connectors (Settings > Connected Apps). It works with both Claude.ai (Cowork) and Claude Code sessions. No local MCP server setup is required.

To verify the connector is working: Ask Claude to call clickup_get_workspace_hierarchy — if it returns your spaces/folders/lists, the connector is live.

If the connector stops working:

  1. Go to claude.ai > Settings > Connected Apps
  2. Check that ClickUp shows as “Connected”
  3. If disconnected, click “Connect” and re-authorize via OAuth
  4. Restart your Claude Code session

Alternative: Self-hosted MCP server (not currently used):

claude mcp add --transport http clickup https://mcp.clickup.com/mcp

This adds ClickUp’s official MCP server directly to Claude Code. Useful if the Claude.ai connector is unavailable. Requires OAuth re-auth on first use.


2. MCP Connector — Complete Tool Reference

The Claude.ai ClickUp connector provides 49 tools across 12 categories.

2.1 Search (1 tool)

ToolWhat It DoesKey Parameters
clickup_searchUniversal search across entire workspace — tasks, docs, dashboards, attachments, whiteboards, chat messages, formskeywords, filters.asset_types (task/doc/whiteboard/dashboard/attachment/chat), filters.assignees, filters.task_statuses (unstarted/active/done/closed/archived), filters.location (projects=Space IDs, categories=Folder IDs, subcategories=List IDs), filters.created_date_from/to, filters.due_date_from/to, sort, count, cursor

Example use cases:

  • “Find all tasks tagged ‘urgent’ assigned to me” → clickup_search with assignee filter
  • “Search for the onboarding doc” → clickup_search with keywords: "onboarding", asset_types: ["doc"]
  • “What tasks are due this week?” → clickup_search with due_date_from/to filters

2.2 Tasks (7 tools)

ToolWhat It DoesKey Parameters
clickup_create_taskCreate a task in a specific listname (required), list_id (required), description, markdown_description, assignees, priority (urgent/high/normal/low), due_date, start_date, tags, status, task_type, parent (for subtasks), custom_fields
clickup_get_taskGet task details by ID (supports custom IDs like ‘DEV-1234’)task_id (required), subtasks (true to include), detail_level (summary/detailed)
clickup_update_taskUpdate any task propertytask_id (required), plus any field to change: name, description, status, priority, due_date, assignees, custom_fields, time_estimate, task_type
clickup_delete_taskDelete a task (always confirm with user first)task_id (required)
clickup_filter_tasksFilter tasks with combined criteria (AND across types, OR within)tags, list_ids, folder_ids, space_ids, statuses, assignees (numeric IDs), due_date_from/to, date_done_from/to, include_closed, order_by, page
clickup_move_taskMove task to a different home listtask_id, list_id (destination)
clickup_add_task_to_listAdd task to an additional list (keeps original)task_id, list_id

Also: clickup_remove_task_from_list — remove from additional list (not home list)

Common workflow — Create a client task:

  1. clickup_get_list with list_name: "Client Tasks" → get list_id
  2. clickup_create_task with name, list_id, assignees: ["me"], priority: "high", due_date: "2026-04-01"

Common workflow — Find and update a task:

  1. clickup_search with keywords: "website redesign" → get task_id
  2. clickup_update_task with task_id, status: "in progress"

2.3 Time Tracking (6 tools)

ToolWhat It DoesKey Parameters
clickup_start_time_trackingStart a timer on a task (only one at a time)task_id (required), description, billable, tags
clickup_stop_time_trackingStop the running timerdescription, tags
clickup_add_time_entryAdd a manual time entrytask_id (required), start (required, YYYY-MM-DD HH:MM), duration (e.g., “1h 30m”) or end_time, description, billable, tags
clickup_get_task_time_entriesGet time entries for one tasktask_id (required), start_date, end_date, is_billable
clickup_get_current_time_entryCheck if a timer is running(no required params)
clickup_get_time_entriesGet time entries across all tasks in a date range (reporting)start_date (required), end_date (required), assignee_id, is_billable

Common workflow — Track time on a task:

  1. clickup_search with keywords: "security assessment" → get task_id
  2. clickup_start_time_tracking with task_id, description: "Initial review"
  3. (work happens)
  4. clickup_stop_time_tracking

Common workflow — Weekly time report:

  1. clickup_get_time_entries with start_date: "2026-03-17", end_date: "2026-03-23"

2.4 Comments (3 tools)

ToolWhat It DoesKey Parameters
clickup_get_task_commentsGet all comments on a task (includes reply_count)task_id (required), start (timestamp for pagination), start_id
clickup_create_task_commentAdd a comment to a tasktask_id (required), comment_text (required), notify_all, assignee
clickup_get_threaded_commentsGet replies to a specific commentcomment_id (required)

2.5 Documents (4 tools)

ToolWhat It DoesKey Parameters
clickup_create_documentCreate a doc in a space/folder/listname (required), parent.id + parent.type (required — 4=space, 5=folder, 6=list, 7=everything, 12=workspace), visibility (PUBLIC/PRIVATE/PERSONAL/HIDDEN), create_page (boolean)
clickup_create_document_pageAdd a page to a documentdocument_id (required), name (required), content (required), content_format (text/md or text/plain), parent_page_id (for sub-pages)
clickup_update_document_pageUpdate page content (WARNING: replaces entire content)document_id (required), page_id (required), content, name, content_format
clickup_list_document_pagesList all pages in a documentdocument_id (required), max_page_depth

Pro Tip: update_document_page does a full replace of content. Always read the page first, then send the full content with your changes included.

2.6 Hierarchy (8 tools)

ToolWhat It DoesKey Parameters
clickup_get_workspace_hierarchyGet spaces → folders → lists treemax_depth (0=spaces, 1=+folders, 2=+lists), limit (max 50), cursor, space_ids
clickup_create_listCreate a list in a spacename (required), space_name or space_id, content, due_date, priority, assignee
clickup_create_list_in_folderCreate a list inside a foldername (required), folder_id (required), content, status
clickup_get_listLook up a list by name or IDlist_name or list_id
clickup_update_listUpdate list name/content/statuslist_id (required), name, content, status
clickup_create_folderCreate a folder in a spacename (required), space_name or space_id, override_statuses
clickup_get_folderLook up a folder by name or IDfolder_id or folder_name + space_name/space_id
clickup_update_folderUpdate folder name/statusesfolder_id (required), name, override_statuses

2.7 Members (3 tools)

ToolWhat It DoesKey Parameters
clickup_get_workspace_membersList all workspace members(no required params)
clickup_find_member_by_nameFind a member by name or emailname_or_email (required)
clickup_resolve_assigneesConvert names/emails/“me” to user IDsassignees array (required) — e.g., ["me"] or ["john@example.com"]

Pro Tip: Most task tools auto-resolve assignees. Use resolve_assignees only when you need IDs separately (e.g., for search filters).

2.8 Tags (2 tools)

ToolWhat It DoesKey Parameters
clickup_add_tag_to_taskAdd an existing tag to a tasktask_id (required), tag_name (required) — tag must already exist in space
clickup_remove_tag_from_taskRemove a tag from a tasktask_id (required), tag_name (required)

Limitation: MCP cannot create new tags — only add/remove existing ones. To create a new tag, use the API or create it manually in ClickUp first.

ToolWhat It DoesKey Parameters
clickup_add_task_dependencySet a dependency between taskstask_id (required), depends_on (required), type: waiting_on or blocking
clickup_remove_task_dependencyRemove a dependencytask_id, depends_on, type
clickup_add_task_linkCreate bidirectional link between taskstask_id (required), links_to (required)
clickup_remove_task_linkRemove a task linktask_id, links_to

Dependency types:

  • waiting_on — task_id cannot start until depends_on is done
  • blocking — task_id is blocking depends_on from starting

2.10 Chat (4 tools)

ToolWhat It DoesKey Parameters
clickup_get_chat_channelsList chat channels with memberslimit (1-100), cursor
clickup_send_chat_messageSend a message to a channelchannel_id (required), content (required), content_format (text/md), type (message/post), assignee, followers
clickup_get_chat_channel_messagesGet messages from a channelchannel_id (required), limit, cursor
clickup_get_chat_message_repliesGet threaded replies to a messagemessage_id (required), limit, cursor

2.11 Custom Fields (1 tool)

ToolWhat It DoesKey Parameters
clickup_get_custom_fieldsGet field definitions at any hierarchy levellist_id, folder_id, space_id, include_workspace (boolean)

Note: To set custom field values, use clickup_create_task or clickup_update_task with the custom_fields array: [{"id": "field_id", "value": "field_value"}]

2.12 Reporting (2 tools)

ToolWhat It DoesKey Parameters
clickup_get_task_time_in_statusTime spent in each status for one tasktask_id (required) — requires “Total time in Status” ClickApp
clickup_get_bulk_tasks_time_in_statusTime in status for up to 100 taskstask_ids array (required, 1-100)

2.13 Reminders (3 tools)

ToolWhat It DoesKey Parameters
clickup_create_reminderCreate a personal remindertitle (required), due_date (required, YYYY-MM-DD or YYYY-MM-DD HH:MM), description
clickup_search_remindersSearch/filter remindersis_overdue, is_completed, reminder_type (REMINDER/ASSIGNED_COMMENT/APPROVAL/etc.), due_date_status (TODO/LATER/DELETED), since, limit, cursor
clickup_update_reminderUpdate a reminderreminder_id (required), title, description, due_date, is_completed

2.14 File Attachments (1 tool)

ToolWhat It DoesKey Parameters
clickup_attach_task_fileAttach a file to a tasktask_id (required), then either: file_data (base64) + file_name, or file_url (http/https) + optional auth_header

3. MCP Connector — Gaps & Limitations

What MCP Cannot Do

Missing CapabilitySolanasis ImpactWorkaround
Goals / OKR managementLow — not currently using GoalsUse API if/when adopting OKRs
Task checklists (sub-items in tasks)Medium — useful for multi-step tasksUse subtasks instead (parent param in create_task), or use task description with markdown checklists
Templates (task/list/folder)Low-Medium — would speed up recurring setupsCreate tasks manually from a “template” task description stored in docs; or use API
Views (create/manage saved views)Low — views are set up once in the UICreate views manually in ClickUp UI
Webhooks (event subscriptions)Medium — needed for real-time automationUse API for webhook setup; or use ClickUp’s built-in automations
Guest/permissions managementLow — one-person operationManage in ClickUp UI when needed
User groupsNone — one personN/A
Space deletionNone — destructive, rareDo it in UI
Tag creationLow — create in UI, apply via MCPCreate tags once in ClickUp UI, then use add_tag_to_task
Checklist items (sub-checklists within tasks)Medium — useful for detailed task breakdownsUse markdown checklists in task description, or subtasks

Free Plan Constraints (if applicable)

  • 100 uses for core time-tracking actions (create, edit, delete)
  • 60 total Custom Field writes (cumulative, never resets)
  • 5 active automations, 100 automation actions/month
  • No native AI on Free (trial only)

See the AI-Native Time Logging Playbook for detailed Free plan analysis.


4. ClickUp API v2 — Complete Reference

Base URL

https://api.clickup.com/api/v2

Authentication

Personal API Token (recommended for Solanasis — single-user):

  1. Go to ClickUp > Settings > Apps > API Token
  2. Click “Generate” or copy existing token
  3. Store in .env file as CLICKUP_API_TOKEN
  4. Include in requests as: Authorization: <your_token>

OAuth 2.0 (for multi-user apps — not needed now):

  • Authorization URL: https://app.clickup.com/api
  • Token URL: https://api.clickup.com/api/v2/oauth/token
  • Standard authorization code grant flow

Rate Limits

PlanRequests/Minute/Token
Free Forever100
Unlimited100
Business100
Business Plus1,000
Enterprise10,000

Headers returned:

  • X-RateLimit-Limit — max requests per minute
  • X-RateLimit-Remaining — requests left in current window
  • X-RateLimit-Reset — Unix timestamp when window resets

When rate limited: HTTP 429 response. Back off and retry after reset time.

Request Format

  • Content-Type: application/json
  • All dates in request bodies: Unix timestamp in milliseconds
  • All dates in query params: Unix timestamp in milliseconds
  • Pagination: Most list endpoints support page (0-indexed) parameter

Error Codes

CodeMeaning
400Bad request — check parameters
401Unauthorized — check API token
403Forbidden — insufficient permissions
404Not found — check IDs
429Rate limited — back off and retry
500Server error — retry with backoff

All 23 Endpoint Categories

#CategoryBase PathCRUDMCP Coverage
1Authorization/oauth/tokenToken managementN/A
2Attachments/task/{task_id}/attachmentUploadYES
3Comments/task/{task_id}/commentCRUDYES
4Custom Task Types/team/{team_id}/custom_itemReadPartial
5Custom Fields/list/{list_id}/fieldRead + SetYES
6Folders/space/{space_id}/folderCRUDYES (no delete)
7Goals/team/{team_id}/goalCRUDNO
8Guests/team/{team_id}/guestCRUDNO
9Lists/folder/{folder_id}/listCRUDYES (no delete)
10Members/task/{task_id}/memberReadYES
11Roles/team/{team_id}/customrolesReadNO
12Shared Hierarchy/team/{team_id}/sharedReadNO
13Spaces/team/{team_id}/spaceCRUDPartial (no delete)
14Tags/space/{space_id}/tagCRUDPartial (no create/delete)
15Tasks/list/{list_id}/taskCRUDYES
16Task Checklists/task/{task_id}/checklistCRUDNO
17Task Relationships/task/{task_id}/dependencyCRUDYES
18Templates/team/{team_id}/taskTemplateRead + ApplyNO
19Time Tracking/team/{team_id}/time_entriesCRUDYES
20User Groups/team/{team_id}/groupCRUDNO
21Users/team/{team_id}/userInvite/Edit/RemoveNO
22Views/team/{team_id}/viewCRUDNO
23Webhooks/team/{team_id}/webhookCRUDNO

5. ClickUp API v2 — Endpoint Deep Dive

5.1 Authorization

When to use API instead of MCP: Only needed if building a multi-user OAuth app.

GET  https://app.clickup.com/api?client_id={id}&redirect_uri={uri}
POST https://api.clickup.com/api/v2/oauth/token

For personal use, just use the API token (no OAuth needed).

5.2 Attachments

When to use API instead of MCP: MCP covers this via clickup_attach_task_file.

POST /api/v2/task/{task_id}/attachment
  • Multipart form upload
  • ?custom_task_ids=true to use custom IDs

5.3 Comments

When to use API instead of MCP: MCP covers task comments. API adds list-level and chat-view comments.

GET    /api/v2/task/{task_id}/comment
POST   /api/v2/task/{task_id}/comment
PUT    /api/v2/comment/{comment_id}
DELETE /api/v2/comment/{comment_id}
GET    /api/v2/list/{list_id}/comment
POST   /api/v2/list/{list_id}/comment
GET    /api/v2/view/{view_id}/comment
POST   /api/v2/view/{view_id}/comment
import requests, os
 
TOKEN = os.getenv("CLICKUP_API_TOKEN")
HEADERS = {"Authorization": TOKEN, "Content-Type": "application/json"}
 
# Create a comment
resp = requests.post(
    f"https://api.clickup.com/api/v2/task/{task_id}/comment",
    headers=HEADERS,
    json={"comment_text": "Status update: Phase 1 complete", "notify_all": True}
)

5.4 Custom Task Types

When to use API instead of MCP: To list all available task types in workspace.

GET /api/v2/team/{team_id}/custom_item

5.5 Custom Fields

When to use API instead of MCP: MCP can read fields and set values. API needed for field-level CRUD (rare).

GET /api/v2/list/{list_id}/field

Free plan warning: 60 total custom field writes (cumulative, never resets).

5.6 Folders

When to use API instead of MCP: MCP covers create/read/update. API adds delete.

GET    /api/v2/space/{space_id}/folder
POST   /api/v2/space/{space_id}/folder
GET    /api/v2/folder/{folder_id}
PUT    /api/v2/folder/{folder_id}
DELETE /api/v2/folder/{folder_id}
# Create a folder
resp = requests.post(
    f"https://api.clickup.com/api/v2/space/{space_id}/folder",
    headers=HEADERS,
    json={"name": "Q2 2026 Client Work"}
)

5.7 Goals (MCP Gap)

Why you need the API: MCP has no goal management at all.

GET    /api/v2/team/{team_id}/goal
POST   /api/v2/team/{team_id}/goal
GET    /api/v2/goal/{goal_id}
PUT    /api/v2/goal/{goal_id}
DELETE /api/v2/goal/{goal_id}
POST   /api/v2/goal/{goal_id}/key_result
PUT    /api/v2/key_result/{key_result_id}
DELETE /api/v2/key_result/{key_result_id}
# Create a goal
resp = requests.post(
    f"https://api.clickup.com/api/v2/team/{team_id}/goal",
    headers=HEADERS,
    json={
        "name": "Q2 Revenue Target",
        "due_date": 1719792000000,  # Unix ms
        "description": "Close 3 new fCIO clients",
        "multiple_owners": False,
        "owners": [user_id],
        "color": "#1DB954"
    }
)
 
# Add a key result
resp = requests.post(
    f"https://api.clickup.com/api/v2/goal/{goal_id}/key_result",
    headers=HEADERS,
    json={
        "name": "Pipeline value > $50K",
        "type": "number",
        "steps_start": 0,
        "steps_end": 50000,
        "unit": "USD"
    }
)

5.8 Guests (MCP Gap)

Why you need the API: MCP cannot manage external user access.

POST   /api/v2/team/{team_id}/guest
GET    /api/v2/team/{team_id}/guest/{guest_id}
PUT    /api/v2/team/{team_id}/guest/{guest_id}
DELETE /api/v2/team/{team_id}/guest/{guest_id}
POST   /api/v2/task/{task_id}/guest/{guest_id}
DELETE /api/v2/task/{task_id}/guest/{guest_id}
# Invite a guest (e.g., a 1099 contractor)
resp = requests.post(
    f"https://api.clickup.com/api/v2/team/{team_id}/guest",
    headers=HEADERS,
    json={"email": "contractor@example.com", "can_edit_tags": False, "can_see_time_spent": True}
)

5.9 Lists

When to use API instead of MCP: MCP covers create/read/update. API adds delete and folderless lists.

GET    /api/v2/folder/{folder_id}/list
POST   /api/v2/folder/{folder_id}/list
GET    /api/v2/space/{space_id}/list          # folderless lists
POST   /api/v2/space/{space_id}/list          # create folderless
GET    /api/v2/list/{list_id}
PUT    /api/v2/list/{list_id}
DELETE /api/v2/list/{list_id}

5.10 Members

When to use API instead of MCP: MCP covers workspace-level members. API adds task/list-level membership.

GET /api/v2/task/{task_id}/member
GET /api/v2/list/{list_id}/member

5.11 Roles (MCP Gap)

Why you need the API: To read custom role definitions.

GET /api/v2/team/{team_id}/customroles

5.12 Shared Hierarchy (MCP Gap)

Why you need the API: To see shared/inherited permissions across the hierarchy.

GET /api/v2/team/{team_id}/shared

5.13 Spaces

When to use API instead of MCP: MCP can read hierarchy. API adds create/update/delete.

GET    /api/v2/team/{team_id}/space
POST   /api/v2/team/{team_id}/space
GET    /api/v2/space/{space_id}
PUT    /api/v2/space/{space_id}
DELETE /api/v2/space/{space_id}

5.14 Tags

When to use API instead of MCP: MCP can add/remove tags from tasks. API adds tag creation and deletion at space level.

GET    /api/v2/space/{space_id}/tag
POST   /api/v2/space/{space_id}/tag
PUT    /api/v2/space/{space_id}/tag/{tag_name}
DELETE /api/v2/space/{space_id}/tag/{tag_name}
# Create a new tag in a space
resp = requests.post(
    f"https://api.clickup.com/api/v2/space/{space_id}/tag",
    headers=HEADERS,
    json={"tag": {"name": "client-acme", "tag_fg": "#FFFFFF", "tag_bg": "#1DB954"}}
)

5.15 Tasks

When to use API instead of MCP: MCP covers most operations. API adds task merging, task from template, and filtered get by list.

GET    /api/v2/list/{list_id}/task           # filtered task list
POST   /api/v2/list/{list_id}/task           # create
GET    /api/v2/task/{task_id}                # get by ID
PUT    /api/v2/task/{task_id}                # update
DELETE /api/v2/task/{task_id}                # delete
GET    /api/v2/team/{team_id}/task           # filtered across workspace

Task from template (MCP gap):

# Create a task from a template
resp = requests.post(
    f"https://api.clickup.com/api/v2/list/{list_id}/taskTemplate/{template_id}",
    headers=HEADERS,
    json={"name": "Security Assessment — Acme Corp"}
)

5.16 Task Checklists (MCP Gap)

Why you need the API: MCP has no checklist support at all.

POST   /api/v2/task/{task_id}/checklist
PUT    /api/v2/checklist/{checklist_id}
DELETE /api/v2/checklist/{checklist_id}
POST   /api/v2/checklist/{checklist_id}/checklist_item
PUT    /api/v2/checklist/{checklist_id}/checklist_item/{checklist_item_id}
DELETE /api/v2/checklist/{checklist_id}/checklist_item/{checklist_item_id}
# Create a checklist on a task
resp = requests.post(
    f"https://api.clickup.com/api/v2/task/{task_id}/checklist",
    headers=HEADERS,
    json={"name": "Security Assessment Steps"}
)
checklist_id = resp.json()["checklist"]["id"]
 
# Add items to the checklist
for item_name in ["Run vulnerability scan", "Review firewall rules", "Check access logs", "Draft findings report"]:
    requests.post(
        f"https://api.clickup.com/api/v2/checklist/{checklist_id}/checklist_item",
        headers=HEADERS,
        json={"name": item_name}
    )

5.17 Task Relationships

When to use API instead of MCP: MCP covers this fully.

POST   /api/v2/task/{task_id}/dependency
DELETE /api/v2/task/{task_id}/dependency
POST   /api/v2/task/{task_id}/link/{links_to}
DELETE /api/v2/task/{task_id}/link/{links_to}

5.18 Templates (MCP Gap)

Why you need the API: MCP cannot list or apply templates.

GET  /api/v2/team/{team_id}/taskTemplate?page=0
POST /api/v2/list/{list_id}/taskTemplate/{template_id}
# List all task templates
resp = requests.get(
    f"https://api.clickup.com/api/v2/team/{team_id}/taskTemplate?page=0",
    headers=HEADERS
)
templates = resp.json()["templates"]
 
# Apply a template to create a task
resp = requests.post(
    f"https://api.clickup.com/api/v2/list/{list_id}/taskTemplate/{template_id}",
    headers=HEADERS,
    json={"name": "DR Verification — Client XYZ"}
)

5.19 Time Tracking

When to use API instead of MCP: MCP covers most operations. API adds edit/delete of individual entries and tag management.

GET    /api/v2/team/{team_id}/time_entries
POST   /api/v2/team/{team_id}/time_entries
GET    /api/v2/team/{team_id}/time_entries/{timer_id}
PUT    /api/v2/team/{team_id}/time_entries/{timer_id}
DELETE /api/v2/team/{team_id}/time_entries/{timer_id}
GET    /api/v2/team/{team_id}/time_entries/current
POST   /api/v2/team/{team_id}/time_entries/start/{task_id}
POST   /api/v2/team/{team_id}/time_entries/stop
GET    /api/v2/team/{team_id}/time_entries/tags
POST   /api/v2/team/{team_id}/time_entries/tags
PUT    /api/v2/team/{team_id}/time_entries/tags

5.20 User Groups (MCP Gap)

Why you need the API: To create/manage teams within the workspace.

GET    /api/v2/team/{team_id}/group
POST   /api/v2/team/{team_id}/group
PUT    /api/v2/group/{group_id}
DELETE /api/v2/group/{group_id}

5.21 Users (MCP Gap)

Why you need the API: To invite, edit roles, or remove workspace members programmatically.

POST   /api/v2/team/{team_id}/user
PUT    /api/v2/team/{team_id}/user/{user_id}
DELETE /api/v2/team/{team_id}/user/{user_id}
GET    /api/v2/team/{team_id}/user/{user_id}

5.22 Views (MCP Gap)

Why you need the API: To programmatically create or manage saved views.

GET    /api/v2/team/{team_id}/view
POST   /api/v2/team/{team_id}/view
GET    /api/v2/view/{view_id}
PUT    /api/v2/view/{view_id}
DELETE /api/v2/view/{view_id}
GET    /api/v2/view/{view_id}/task

Also available at space, folder, and list levels:

GET/POST /api/v2/space/{space_id}/view
GET/POST /api/v2/folder/{folder_id}/view
GET/POST /api/v2/list/{list_id}/view
# Create a "My Open Tasks" view
resp = requests.post(
    f"https://api.clickup.com/api/v2/space/{space_id}/view",
    headers=HEADERS,
    json={
        "name": "My Open Tasks",
        "type": "list",
        "grouping": {"field": "status"},
        "filters": {
            "op": "AND",
            "fields": [{"field": "assignee", "op": "=", "value": [user_id]}]
        }
    }
)

5.23 Webhooks (MCP Gap)

Why you need the API: To set up event-driven automation.

GET    /api/v2/team/{team_id}/webhook
POST   /api/v2/team/{team_id}/webhook
PUT    /api/v2/webhook/{webhook_id}
DELETE /api/v2/webhook/{webhook_id}
# Create a webhook for task status changes
resp = requests.post(
    f"https://api.clickup.com/api/v2/team/{team_id}/webhook",
    headers=HEADERS,
    json={
        "endpoint": "https://your-endpoint.example.com/clickup-webhook",
        "events": ["taskStatusUpdated", "taskCreated", "taskDeleted"],
        "space_id": space_id  # scope to a specific space
    }
)
webhook_secret = resp.json()["webhook"]["secret"]
# Store webhook_secret securely for signature verification

See Section 7 for full webhook details.


6. Python Integration Patterns

Project Structure

solanasis-scripts/
└── clickup/
    ├── __init__.py
    ├── client.py          # Base client with auth + rate limiting
    ├── goals.py           # Goal/OKR operations
    ├── checklists.py      # Checklist operations
    ├── templates.py       # Template operations
    └── webhooks.py        # Webhook management

Auth Setup

.env file (in solanasis-scripts/):

CLICKUP_API_TOKEN=pk_12345678_ABCDEFGHIJKLMNOPQRSTUVWXYZ
CLICKUP_TEAM_ID=12345678

Security: Never commit .env to git. Verify .gitignore includes it.

Base Client

"""solanasis-scripts/clickup/client.py — ClickUp API base client"""
 
import os
import time
import requests
from dotenv import load_dotenv
 
load_dotenv()
 
CLICKUP_API_TOKEN = os.getenv("CLICKUP_API_TOKEN")
CLICKUP_TEAM_ID = os.getenv("CLICKUP_TEAM_ID")
BASE_URL = "https://api.clickup.com/api/v2"
 
 
def _headers():
    return {
        "Authorization": CLICKUP_API_TOKEN,
        "Content-Type": "application/json",
    }
 
 
def clickup_request(method, path, **kwargs):
    """Make a ClickUp API request with automatic rate limit handling.
 
    Args:
        method: HTTP method (get, post, put, delete)
        path: API path (e.g., "/task/abc123")
        **kwargs: Passed to requests.request (json, params, etc.)
 
    Returns:
        Response JSON or None for 204s
 
    Raises:
        requests.HTTPError: On non-retryable errors
    """
    url = f"{BASE_URL}{path}"
    max_retries = 3
 
    for attempt in range(max_retries):
        resp = requests.request(method, url, headers=_headers(), **kwargs)
 
        if resp.status_code == 429:
            reset_time = int(resp.headers.get("X-RateLimit-Reset", time.time() + 60))
            wait = max(reset_time - time.time(), 1)
            print(f"Rate limited. Waiting {wait:.0f}s...")
            time.sleep(wait)
            continue
 
        resp.raise_for_status()
 
        if resp.status_code == 204:
            return None
        return resp.json()
 
    raise Exception("Rate limit retries exhausted")
 
 
# Convenience wrappers
def get(path, **kwargs):
    return clickup_request("GET", path, **kwargs)
 
def post(path, **kwargs):
    return clickup_request("POST", path, **kwargs)
 
def put(path, **kwargs):
    return clickup_request("PUT", path, **kwargs)
 
def delete(path, **kwargs):
    return clickup_request("DELETE", path, **kwargs)

Example: Goals Script

"""solanasis-scripts/clickup/goals.py — Goal/OKR management"""
 
from client import get, post, put, delete, CLICKUP_TEAM_ID
 
 
def list_goals():
    return get(f"/team/{CLICKUP_TEAM_ID}/goal")
 
 
def create_goal(name, description="", due_date_ms=None):
    payload = {"name": name, "description": description}
    if due_date_ms:
        payload["due_date"] = due_date_ms
    return post(f"/team/{CLICKUP_TEAM_ID}/goal", json=payload)
 
 
def add_key_result(goal_id, name, target_type="number", start=0, end=100, unit=""):
    return post(f"/goal/{goal_id}/key_result", json={
        "name": name,
        "type": target_type,
        "steps_start": start,
        "steps_end": end,
        "unit": unit
    })
 
 
if __name__ == "__main__":
    goals = list_goals()
    for g in goals.get("goals", []):
        print(f"  {g['name']}{g.get('percent_completed', 0)}%")

Example: Checklists Script

"""solanasis-scripts/clickup/checklists.py — Checklist operations"""
 
from client import post, put, delete
 
 
def create_checklist(task_id, name):
    resp = post(f"/task/{task_id}/checklist", json={"name": name})
    return resp["checklist"]["id"]
 
 
def add_item(checklist_id, name, assignee_id=None):
    payload = {"name": name}
    if assignee_id:
        payload["assignee"] = assignee_id
    return post(f"/checklist/{checklist_id}/checklist_item", json=payload)
 
 
def check_item(checklist_id, item_id, resolved=True):
    return put(f"/checklist/{checklist_id}/checklist_item/{item_id}",
               json={"resolved": resolved})
 
 
def create_checklist_from_list(task_id, checklist_name, items):
    """Create a checklist and populate it with items.
 
    Args:
        task_id: ClickUp task ID
        checklist_name: Name for the checklist
        items: List of item name strings
    """
    cl_id = create_checklist(task_id, checklist_name)
    for item in items:
        add_item(cl_id, item)
    return cl_id

Date Conversion Helper

The ClickUp API uses Unix timestamps in milliseconds. Python helper:

from datetime import datetime
 
def to_clickup_ms(date_str):
    """Convert 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM' to Unix ms."""
    for fmt in ("%Y-%m-%d %H:%M", "%Y-%m-%d"):
        try:
            dt = datetime.strptime(date_str, fmt)
            return int(dt.timestamp() * 1000)
        except ValueError:
            continue
    raise ValueError(f"Invalid date format: {date_str}")
 
def from_clickup_ms(ms):
    """Convert Unix ms to 'YYYY-MM-DD HH:MM'."""
    return datetime.fromtimestamp(ms / 1000).strftime("%Y-%m-%d %H:%M")

7. Webhooks & Automation

How ClickUp Webhooks Work

  1. You register a webhook URL with ClickUp (via API)
  2. ClickUp sends POST requests to your URL when events occur
  3. Each request includes a signature for verification
  4. You process the event and take action

Available Event Types (30+)

Task events:

  • taskCreated, taskUpdated, taskDeleted
  • taskStatusUpdated, taskPriorityUpdated
  • taskAssigneeUpdated, taskDueDateUpdated
  • taskTagUpdated, taskMoved
  • taskTimeEstimateUpdated, taskTimeTrackedUpdated

Comment events:

  • taskCommentPosted, taskCommentUpdated

List/Folder/Space events:

  • listCreated, listUpdated, listDeleted
  • folderCreated, folderUpdated, folderDeleted
  • spaceCreated, spaceUpdated, spaceDeleted

Goal events:

  • goalCreated, goalUpdated, goalDeleted
  • keyResultCreated, keyResultUpdated, keyResultDeleted

Webhook Registration

from client import post, get, delete, CLICKUP_TEAM_ID
 
 
def create_webhook(endpoint_url, events, space_id=None, folder_id=None, list_id=None):
    """Register a webhook. Scope with space/folder/list IDs, or omit for workspace-wide."""
    payload = {"endpoint": endpoint_url, "events": events}
    if space_id:
        payload["space_id"] = space_id
    if folder_id:
        payload["folder_id"] = folder_id
    if list_id:
        payload["list_id"] = list_id
 
    resp = post(f"/team/{CLICKUP_TEAM_ID}/webhook", json=payload)
    return resp["webhook"]  # includes "id" and "secret"
 
 
def list_webhooks():
    return get(f"/team/{CLICKUP_TEAM_ID}/webhook")
 
 
def delete_webhook(webhook_id):
    return delete(f"/webhook/{webhook_id}")

Signature Verification

ClickUp signs webhook payloads with HMAC-SHA256 using the webhook secret:

import hmac
import hashlib
 
 
def verify_webhook_signature(payload_body, signature_header, webhook_secret):
    """Verify that a webhook payload is authentic.
 
    Args:
        payload_body: Raw request body (bytes)
        signature_header: Value of X-Signature header
        webhook_secret: Secret from webhook creation response
 
    Returns:
        True if signature is valid
    """
    expected = hmac.new(
        webhook_secret.encode(),
        payload_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

When Webhooks Make Sense vs. Polling

ApproachUse When
WebhooksYou need real-time reactions (e.g., auto-notify when a task is completed)
Polling (MCP)You need information on-demand during Claude Code sessions
Polling (API script)Scheduled reports or batch processing (e.g., daily time summary)

For Solanasis right now: Polling via MCP is sufficient. Webhooks become valuable when you need automated reactions without Claude Code being active (e.g., a task completion triggers an email to a client). This requires a server/endpoint to receive webhooks — consider Cloudflare Workers (fits your existing Cloudflare stack).


8. Troubleshooting & Maintenance

MCP Connector Issues

ProblemFix
MCP tools not appearingCheck claude.ai > Settings > Connected Apps > ClickUp is “Connected"
"Unauthorized” errorsRe-authorize: disconnect and reconnect ClickUp in claude.ai settings
Tools appear but calls failClickUp may be having an outage — check status.clickup.com
Slow responsesNormal — MCP routes through claude.ai servers. Large workspace queries take longer.
Missing workspace dataCheck clickup_get_workspace_hierarchy — you may need to paginate (cursor param)

API Script Issues

ProblemFix
401 UnauthorizedCheck CLICKUP_API_TOKEN in .env — regenerate if expired
429 Rate LimitedThe base client auto-retries. If persistent, reduce request frequency
404 Not FoundVerify task/list/space IDs. Custom IDs need ?custom_task_ids=true param
Missing data in responsesCheck pagination — most list endpoints default to page 0 only
SSL/connection errorsCheck network connectivity. ClickUp API requires HTTPS.

Rate Limit Strategy

  1. The MCP connector handles rate limits internally — no action needed
  2. For API scripts, the base client (Section 6) includes automatic retry with backoff
  3. If hitting limits frequently:
    • Batch operations where possible
    • Cache responses that don’t change often (workspace hierarchy, member lists)
    • Consider upgrading ClickUp plan for higher limits (Business+: 1,000/min)

API Version Migration (v2 → v3)

ClickUp is incrementally migrating endpoints to v3. As of March 2026:

  • Most endpoints remain v2
  • Some endpoints have v3 equivalents
  • No hard cutover date announced
  • The v2 endpoints continue to work
  • Monitor ClickUp developer docs for migration timeline

Action: No immediate changes needed. When building new scripts, check if a v3 endpoint exists. The base URL for v3 is https://api.clickup.com/api/v3.

ClickUp Plan Considerations

PlanPriceKey Limits for Solanasis
Free Forever$0100 time tracking uses, 60 custom field writes (never resets), 5 automations
Unlimited$10/user/moUnlimited time tracking, unlimited custom fields, unlimited automations
Business$19/user/mo+ advanced features, same API rate limits
Business Plus$29/user/mo+ 1,000 req/min API rate limit

Recommendation from time-logging playbook: Upgrade to Unlimited when the workflow sticks. The Free plan constraints are real, especially the 60-write custom field cap.


Appendix: Quick Reference Card

Most-Used MCP Tools

Search:     clickup_search keywords="..." filters.asset_types=["task"]
Create:     clickup_create_task name="..." list_id="..."
Update:     clickup_update_task task_id="..." status="in progress"
Find:       clickup_filter_tasks tags=["client-acme"] statuses=["open"]
Timer:      clickup_start_time_tracking task_id="..."
Stop:       clickup_stop_time_tracking
Comment:    clickup_create_task_comment task_id="..." comment_text="..."
Hierarchy:  clickup_get_workspace_hierarchy max_depth=2

API Authentication Header

Authorization: pk_12345678_ABCDEFGHIJKLMNOPQRSTUVWXYZ
Content-Type: application/json

Date Formats

  • MCP tools: YYYY-MM-DD or YYYY-MM-DD HH:MM (human-readable)
  • API requests: Unix timestamp in milliseconds (e.g., 1711238400000)
  • Conversion: int(datetime.strptime("2026-03-24", "%Y-%m-%d").timestamp() * 1000)

Python SDK Options (for reference)

PackageVersionStatusLicense
clickup-python-sdk2.0.1Beta, single maintainerGPLv3
pyclickupolderDocumented but less maintainedMIT
Raw requestsstableRecommended — most resilientN/A

Recommendation: Use raw requests with the base client from Section 6. More resilient than SDKs with single maintainers.