ClickUp Operator

  • src
  • clickup_operator
import httpx from typing import Optional, List, Dict, Any, Union class ClickUpAPI: def __init__(self, api_key: str): self.api_key = api_key self.base_url = "https://api.clickup.com/api/v2" self.headers = { "Authorization": api_key, "Content-Type": "application/json" } self._setup_client() def _setup_client(self): """Setup the HTTP client with proper timeout and retry settings""" timeout = httpx.Timeout(30.0, connect=10.0) # 30s timeout, 10s connect timeout limits = httpx.Limits(max_keepalive_connections=5, max_connections=10) self.client = httpx.AsyncClient( headers=self.headers, timeout=timeout, limits=limits ) async def get_teams(self) -> List[Dict[str, Any]]: """Get all accessible teams/workspaces""" response = await self.client.get(f"{self.base_url}/team") response.raise_for_status() return response.json()["teams"] async def get_spaces(self, team_id: str): """Get spaces in a team""" response = await self.client.get(f"{self.base_url}/team/{team_id}/space") response.raise_for_status() return response.json()["spaces"] async def create_space(self, team_id: str, name: str, **kwargs): """Create a space""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/team/{team_id}/space", json=data ) response.raise_for_status() return response.json() async def get_lists(self, space_id: str): """Get lists in a space""" response = await self.client.get(f"{self.base_url}/space/{space_id}/list") response.raise_for_status() return response.json()["lists"] async def create_list(self, space_id: str, name: str, **kwargs): """Create a list""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/space/{space_id}/list", json=data ) response.raise_for_status() return response.json() async def create_task(self, list_id: str, name: str, **kwargs): """Create a task""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/list/{list_id}/task", json=data ) response.raise_for_status() return response.json() async def get_tasks(self, list_id: str, **kwargs): """Get tasks from a list""" response = await self.client.get( f"{self.base_url}/list/{list_id}/task", params=kwargs ) response.raise_for_status() return response.json()["tasks"] async def update_task(self, task_id: str, **kwargs): """Update a task""" response = await self.client.put( f"{self.base_url}/task/{task_id}", json=kwargs ) response.raise_for_status() return response.json() async def create_task_comment(self, task_id: str, comment_text: str, **kwargs): """Create a comment on a task""" data = {"comment_text": comment_text, **kwargs} response = await self.client.post( f"{self.base_url}/task/{task_id}/comment", json=data ) response.raise_for_status() return response.json() async def start_time_entry(self, task_id: str, **kwargs): """Start time tracking for a task""" response = await self.client.post( f"{self.base_url}/task/{task_id}/time", json=kwargs ) response.raise_for_status() return response.json() async def stop_time_entry(self, task_id: str, **kwargs): """Stop time tracking for a task""" response = await self.client.put( f"{self.base_url}/task/{task_id}/time", json=kwargs ) response.raise_for_status() return response.json() async def get_accessible_custom_fields(self, list_id: str): """Get custom fields accessible in a list""" response = await self.client.get( f"{self.base_url}/list/{list_id}/field" ) response.raise_for_status() return response.json()["fields"] async def create_checklist(self, task_id: str, name: str): """Create a checklist in a task""" response = await self.client.post( f"{self.base_url}/task/{task_id}/checklist", json={"name": name} ) response.raise_for_status() return response.json() async def edit_checklist(self, checklist_id: str, name: str): """Edit a checklist""" response = await self.client.put( f"{self.base_url}/checklist/{checklist_id}", json={"name": name} ) response.raise_for_status() return response.json() async def delete_checklist(self, checklist_id: str): """Delete a checklist""" response = await self.client.delete(f"{self.base_url}/checklist/{checklist_id}") response.raise_for_status() return response.json() async def create_checklist_item(self, checklist_id: str, name: str, **kwargs): """Create an item in a checklist""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/checklist/{checklist_id}/checklist_item", json=data ) response.raise_for_status() return response.json() async def get_space_tags(self, space_id: str): """Get all tags in a space""" response = await self.client.get(f"{self.base_url}/space/{space_id}/tag") response.raise_for_status() return response.json()["tags"] async def create_space_tag(self, space_id: str, name: str, **kwargs): """Create a new tag in a space""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/space/{space_id}/tag", json=data ) response.raise_for_status() return response.json() async def get_task_watchers(self, task_id: str): """Get task watchers""" response = await self.client.get(f"{self.base_url}/task/{task_id}/watching") response.raise_for_status() return response.json()["watchers"] async def add_task_watcher(self, task_id: str, watcher_id: str): """Add a watcher to a task""" response = await self.client.post( f"{self.base_url}/task/{task_id}/watching", json={"watcher_id": watcher_id} ) response.raise_for_status() return response.json() async def add_task_dependency(self, task_id: str, depends_on: str, dependency_type: str = "waiting_on") -> dict: """Add a dependency to a task.""" data = { "depends_on": depends_on, "dependency_type": dependency_type } response = await self.client.post(f"{self.base_url}/task/{task_id}/dependency", json=data) response.raise_for_status() return response.json() async def get_task_dependencies(self, task_id: str) -> dict: """Get dependencies for a task.""" response = await self.client.get(f"{self.base_url}/task/{task_id}/dependency") response.raise_for_status() return response.json()["dependencies"] async def delete_comment(self, comment_id: str) -> dict: """Delete a comment.""" response = await self.client.delete(f"{self.base_url}/comment/{comment_id}") response.raise_for_status() return response.json() async def get_comments(self, task_id: str) -> list: """Get comments for a task.""" response = await self.client.get(f"{self.base_url}/task/{task_id}/comment") response.raise_for_status() return response.json()["comments"] async def update_comment(self, comment_id: str, comment_text: str, **kwargs) -> dict: """Update a comment.""" data = {"comment_text": comment_text, **kwargs} response = await self.client.put(f"{self.base_url}/comment/{comment_id}", json=data) response.raise_for_status() return response.json() async def remove_task_dependency(self, task_id: str, dependency_id: str) -> dict: """Remove a dependency from a task.""" response = await self.client.delete(f"{self.base_url}/task/{task_id}/dependency/{dependency_id}") response.raise_for_status() return response.json() async def get_authorized_user(self): """Get information about the currently authorized user""" response = await self.client.get(f"{self.base_url}/user") response.raise_for_status() return response.json()["user"] async def create_folder(self, space_id: str, name: str, **kwargs): """Create a folder in a space""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/space/{space_id}/folder", json=data ) response.raise_for_status() return response.json() async def create_folderless_list(self, space_id: str, name: str, **kwargs): """Create a list directly in a space without a folder""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/space/{space_id}/list", json=data ) response.raise_for_status() return response.json() async def create_goal(self, team_id: str, name: str, **kwargs): """Create a goal in a team""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/team/{team_id}/goal", json=data ) response.raise_for_status() return response.json() async def create_key_result(self, goal_id: str, name: str, **kwargs): """Create a key result for a goal""" data = {"name": name, **kwargs} response = await self.client.post( f"{self.base_url}/goal/{goal_id}/key_result", json=data ) response.raise_for_status() return response.json() async def invite_guest(self, team_id: str, email: str, **kwargs: Any) -> Dict[str, Any]: """Invite a guest to workspace""" data = {"email": email, **kwargs} response = await self.client.post( f"{self.base_url}/team/{team_id}/guest", json=data ) response.raise_for_status() return response.json() async def get_task_members(self, task_id: str) -> List[Dict[str, Any]]: """Get members of a task""" response = await self.client.get( f"{self.base_url}/task/{task_id}/member" ) response.raise_for_status() return response.json()["members"] async def get_list_members(self, list_id: str) -> List[Dict[str, Any]]: """Get members of a list""" response = await self.client.get( f"{self.base_url}/list/{list_id}/member" ) response.raise_for_status() return response.json()["members"] async def get_custom_roles(self, team_id: str) -> List[Dict[str, Any]]: """Get custom roles in a workspace""" response = await self.client.get( f"{self.base_url}/team/{team_id}/customRoles" ) response.raise_for_status() return response.json()["roles"] async def get_task_templates(self, team_id: str, page: int = 0) -> List[Dict[str, Any]]: """Get task templates""" response = await self.client.get( f"{self.base_url}/team/{team_id}/taskTemplate", params={"page": page} ) response.raise_for_status() return response.json()["templates"] async def create_task_from_template(self, list_id: str, template_id: str, name: str): """Create a task from a template""" response = await self.client.post( f"{self.base_url}/list/{list_id}/taskTemplate/{template_id}", json={"name": name} ) response.raise_for_status() return response.json() async def get_time_entries(self, team_id: str, start_date: Optional[int] = None, end_date: Optional[int] = None, **kwargs: Any) -> List[Dict[str, Any]]: """Get time entries within a date range""" params = {k: v for k, v in kwargs.items()} if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date response = await self.client.get( f"{self.base_url}/team/{team_id}/time_entries", params=params ) response.raise_for_status() return response.json()["data"] async def get_time_entry_history(self, team_id: str, timer_id: str) -> List[Dict[str, Any]]: """Get time entry history""" response = await self.client.get( f"{self.base_url}/team/{team_id}/time_entries/{timer_id}/history" ) response.raise_for_status() return response.json()["data"] async def get_single_time_entry(self, team_id: str, time_entry_id: str) -> Dict[str, Any]: """Get a single time entry""" response = await self.client.get( f"{self.base_url}/team/{team_id}/time_entries/{time_entry_id}" ) response.raise_for_status() return response.json() async def get_running_time_entry(self, team_id: str) -> Dict[str, Any]: """Get currently running time entry""" response = await self.client.get( f"{self.base_url}/team/{team_id}/time_entries/current" ) response.raise_for_status() return response.json() async def create_time_entry(self, team_id: str, **kwargs: Any) -> Dict[str, Any]: """Create a time entry""" response = await self.client.post( f"{self.base_url}/team/{team_id}/time_entries", json=kwargs ) response.raise_for_status() return response.json() async def update_time_entry(self, team_id: str, timer_id: str, **kwargs: Any) -> Dict[str, Any]: """Update a time entry""" response = await self.client.put( f"{self.base_url}/team/{team_id}/time_entries/{timer_id}", json=kwargs ) response.raise_for_status() return response.json() async def delete_time_entry(self, team_id: str, time_entry_id: str) -> Dict[str, Any]: """Delete a time entry""" response = await self.client.delete( f"{self.base_url}/team/{team_id}/time_entries/{time_entry_id}" ) response.raise_for_status() return response.json() async def start_time_entry_v2(self, team_id: str, timer_id: str) -> Dict[str, Any]: """Start a time entry (v2)""" response = await self.client.post( f"{self.base_url}/team/{team_id}/time_entries/start/{timer_id}" ) response.raise_for_status() return response.json() async def stop_time_entry_v2(self, team_id: str) -> Dict[str, Any]: """Stop current time entry (v2)""" response = await self.client.post( f"{self.base_url}/team/{team_id}/time_entries/stop" ) response.raise_for_status() return response.json() # Time Entry Tags async def get_time_entry_tags(self, team_id: str) -> Dict[str, Any]: """Get all tags from time entries""" response = await self.client.get( f"{self.base_url}/team/{team_id}/time_entries/tags" ) response.raise_for_status() return response.json() async def add_tags_to_time_entries(self, team_id: str, time_entry_ids: List[str], tags: List[Dict[str, str]]) -> Dict[str, Any]: """Add tags to time entries""" data = { "time_entry_ids": time_entry_ids, "tags": tags } response = await self.client.post( f"{self.base_url}/team/{team_id}/time_entries/tags", json=data ) response.raise_for_status() return response.json() async def remove_tags_from_time_entries(self, team_id: str, time_entry_ids: List[str], tags: List[Dict[str, str]]) -> Dict[str, Any]: """Remove tags from time entries""" data = { "time_entry_ids": time_entry_ids, "tags": tags } response = await self.client.delete( f"{self.base_url}/team/{team_id}/time_entries/tags", params=data # Changed from json to params for DELETE request ) response.raise_for_status() return response.json() async def update_time_entry_tag(self, team_id: str, name: str, new_name: str, tag_bg: str, tag_fg: str) -> Dict[str, Any]: """Change tag names from time entries""" data = { "name": name, "new_name": new_name, "tag_bg": tag_bg, "tag_fg": tag_fg } response = await self.client.put( f"{self.base_url}/team/{team_id}/time_entries/tags", json=data ) response.raise_for_status() return response.json() # Guests async def edit_guest(self, team_id: str, guest_id: str, **kwargs: Any) -> Dict[str, Any]: """Edit a guest on workspace""" response = await self.client.put( f"{self.base_url}/team/{team_id}/guest/{guest_id}", json=kwargs ) response.raise_for_status() return response.json() async def remove_guest(self, team_id: str, guest_id: str) -> Dict[str, Any]: """Remove a guest from workspace""" response = await self.client.delete( f"{self.base_url}/team/{team_id}/guest/{guest_id}" ) response.raise_for_status() return response.json() async def get_guest(self, team_id: str, guest_id: str) -> Dict[str, Any]: """Get guest information""" response = await self.client.get( f"{self.base_url}/team/{team_id}/guest/{guest_id}" ) response.raise_for_status() return response.json() async def add_guest_to_task(self, task_id: str, guest_id: str, permission_level: str) -> Dict[str, Any]: """Add a guest to a task""" data = {"permission_level": permission_level} response = await self.client.post( f"{self.base_url}/task/{task_id}/guest/{guest_id}", json=data ) response.raise_for_status() return response.json() async def remove_guest_from_task(self, task_id: str, guest_id: str) -> Dict[str, Any]: """Remove a guest from a task""" response = await self.client.delete( f"{self.base_url}/task/{task_id}/guest/{guest_id}" ) response.raise_for_status() return response.json() async def add_guest_to_list(self, list_id: str, guest_id: str, permission_level: str) -> Dict[str, Any]: """Add a guest to a list""" data = {"permission_level": permission_level} response = await self.client.post( f"{self.base_url}/list/{list_id}/guest/{guest_id}", json=data ) response.raise_for_status() return response.json() async def remove_guest_from_list(self, list_id: str, guest_id: str) -> Dict[str, Any]: """Remove a guest from a list""" response = await self.client.delete( f"{self.base_url}/list/{list_id}/guest/{guest_id}" ) response.raise_for_status() return response.json() async def add_guest_to_folder(self, folder_id: str, guest_id: str, permission_level: str) -> Dict[str, Any]: """Add a guest to a folder""" data = {"permission_level": permission_level} response = await self.client.post( f"{self.base_url}/folder/{folder_id}/guest/{guest_id}", json=data ) response.raise_for_status() return response.json() async def remove_guest_from_folder(self, folder_id: str, guest_id: str) -> Dict[str, Any]: """Remove a guest from a folder""" response = await self.client.delete( f"{self.base_url}/folder/{folder_id}/guest/{guest_id}" ) response.raise_for_status() return response.json() # Views async def create_team_view(self, team_id: str, **kwargs: Any) -> Dict[str, Any]: """Create a team view""" response = await self.client.post( f"{self.base_url}/team/{team_id}/view", json=kwargs ) response.raise_for_status() return response.json() # Teams (User Groups) async def create_team_group(self, team_id: str, name: str, member_ids: List[int]) -> Dict[str, Any]: """Create a team (user group)""" data = { "name": name, "member_ids": member_ids } response = await self.client.post( f"{self.base_url}/team/{team_id}/group", json=data ) response.raise_for_status() return response.json() async def get_team_groups(self, team_id: str) -> Dict[str, Any]: """Get teams (user groups)""" response = await self.client.get( f"{self.base_url}/team/{team_id}/group" ) response.raise_for_status() return response.json() async def update_team_group(self, group_id: str, **kwargs: Any) -> Dict[str, Any]: """Update a team (user group)""" response = await self.client.put( f"{self.base_url}/group/{group_id}", json=kwargs ) response.raise_for_status() return response.json() async def delete_team_group(self, group_id: str) -> Dict[str, Any]: """Delete a team (user group)""" response = await self.client.delete( f"{self.base_url}/group/{group_id}" ) response.raise_for_status() return response.json() # Teams (Workspaces) async def get_workspace_seats(self, workspace_id: str) -> Dict[str, Any]: """Get workspace seats""" response = await self.client.get( f"{self.base_url}/team/{workspace_id}/seats" ) response.raise_for_status() return response.json() # Bulk Operations async def get_task_time_in_status(self, task_id: str, **kwargs: Any) -> Dict[str, Any]: """Get task's time in status""" response = await self.client.get( f"{self.base_url}/task/{task_id}/time_in_status", params=kwargs ) response.raise_for_status() return response.json() async def get_bulk_tasks_time_in_status(self, task_ids: List[str], **kwargs: Any) -> Dict[str, Any]: """Get bulk tasks' time in status""" task_ids_str = ",".join(task_ids) response = await self.client.get( f"{self.base_url}/task/bulk_time_in_status/{task_ids_str}", params=kwargs ) response.raise_for_status() return response.json() # Shared Hierarchy async def get_shared_hierarchy(self, team_id: str) -> Dict[str, Any]: """Get shared hierarchy""" response = await self.client.get( f"{self.base_url}/team/{team_id}/shared" ) response.raise_for_status() return response.json() # Users async def invite_user(self, team_id: str, email: str, **kwargs: Any) -> Dict[str, Any]: """Invite a user to a workspace""" data = {"email": email, **kwargs} response = await self.client.post( f"{self.base_url}/team/{team_id}/user", json=data ) response.raise_for_status() return response.json() async def edit_user(self, team_id: str, user_id: int, **kwargs: Any) -> Dict[str, Any]: """Edit a user in a workspace""" response = await self.client.put( f"{self.base_url}/team/{team_id}/user/{user_id}", json=kwargs ) response.raise_for_status() return response.json() async def remove_user(self, team_id: str, user_id: int) -> Dict[str, Any]: """Remove a user from a workspace""" response = await self.client.delete( f"{self.base_url}/team/{team_id}/user/{user_id}" ) response.raise_for_status() return response.json() async def get_user(self, team_id: str, user_id: int) -> Dict[str, Any]: """Get user information""" response = await self.client.get( f"{self.base_url}/team/{team_id}/user/{user_id}" ) response.raise_for_status() return response.json() # Webhooks async def create_webhook(self, team_id: str, endpoint: str, events: List[str], **kwargs: Any) -> Dict[str, Any]: """Create a webhook""" data = { "endpoint": endpoint, "events": events, **kwargs } response = await self.client.post( f"{self.base_url}/team/{team_id}/webhook", json=data ) response.raise_for_status() return response.json() async def get_webhooks(self, team_id: str) -> Dict[str, Any]: """Get webhooks""" response = await self.client.get( f"{self.base_url}/team/{team_id}/webhook" ) response.raise_for_status() return response.json() async def update_webhook(self, webhook_id: str, **kwargs: Any) -> Dict[str, Any]: """Update a webhook""" response = await self.client.put( f"{self.base_url}/webhook/{webhook_id}", json=kwargs ) response.raise_for_status() return response.json() async def delete_webhook(self, webhook_id: str) -> Dict[str, Any]: """Delete a webhook""" response = await self.client.delete( f"{self.base_url}/webhook/{webhook_id}" ) response.raise_for_status() return response.json() async def __aenter__(self) -> "ClickUpAPI": """Async context manager entry""" return self async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: """Async context manager exit""" await self.client.aclose()