Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,80 @@
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../..';
import { createAction } from '@activepieces/pieces-framework';
export const createClickupChannelInSpaceFolderOrList = createAction({
auth: clickupAuth,
name: 'create_channel_in_space_folder_list',
description:
'Creates a channel in a ClickUp workspace in a space, folder or list',
displayName: 'Create Channel in Space/Folder/List',
props: {
workspace_id: clickupCommon.workspace_id(),
description: Property.ShortText({
description: 'Description of the channel',
displayName: 'Channel Description',
required: false,
defaultValue: '',
}),
topic: Property.ShortText({
description: 'Topic of the channel',
displayName: 'Channel Topic',
required: false,
defaultValue: '',
}),
locationType: Property.StaticDropdown({
description: 'Type of location',
displayName: 'Location Type',
required: true,
options: {
options: [
{ label: 'Folder', value: 'folder' },
{ label: 'Space', value: 'space' },
{ label: 'List', value: 'list' },
],
},
defaultValue: 'folder',
}),
locationId: Property.ShortText({
description: 'ID of the location',
displayName: 'Location ID',
required: true,
}),
// TODO: add user ids
visibility: Property.StaticDropdown({
description: 'Visibility of the channel',
displayName: 'Channel Visibility',
required: true,
options: {
options: [
{ label: 'Public', value: 'PUBLIC' },
{ label: 'Private', value: 'PRIVATE' },
],
},
defaultValue: 'public',
}),
},
async run(configValue) {
const { workspace_id, description, visibility, locationType, locationId,topic } =
configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.POST,
`workspaces/${workspace_id}/chat/channels/location`,
getAccessTokenOrThrow(configValue.auth),
{
topic,
description,
visibility,
location: {
id: locationId,
type: locationType,
},
},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,64 @@
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
import { createAction } from '@activepieces/pieces-framework';
export const createClickupChannel = createAction({
auth: clickupAuth,
name: 'create_channel',
description: 'Creates a channel in a ClickUp workspace',
displayName: 'Create Channel',
props: {
workspace_id: clickupCommon.workspace_id(),
name: Property.ShortText({
description: 'Name of the channel',
displayName: 'Channel Name',
required: true,
defaultValue: '',
}),
description: Property.ShortText({
description: 'Description of the channel',
displayName: 'Channel Description',
required: false,
defaultValue: '',
}),
topic: Property.ShortText({
description: 'Topic of the channel',
displayName: 'Channel Topic',
required: false,
defaultValue: '',
}),
// TODO: add user ids
visibility: Property.StaticDropdown({
description: 'Visibility of the channel',
displayName: 'Channel Visibility',
required: true,
options: {
options: [
{ label: 'Public', value: 'PUBLIC' },
{ label: 'Private', value: 'PRIVATE' },
],
},
defaultValue: 'public',
}),
},
async run(configValue) {
const { workspace_id, name, description, visibility,topic } =
configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.POST,
`workspaces/${workspace_id}/chat/channels`,
getAccessTokenOrThrow(configValue.auth),
{
name,
topic,
description,
visibility,
},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,37 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const createClickupMessageReaction = createAction({
auth: clickupAuth,
name: 'create_message_reaction',
description: 'Creates a reaction to a message in a ClickUp channel',
displayName: 'Create Message Reaction',
props: {
workspace_id: clickupCommon.workspace_id(),
message_id: Property.ShortText({
description: 'ID of the message to create reaction for',
displayName: 'Message ID',
required: true,
}),
emoji: Property.ShortText({
description: 'Emoji to react with',
displayName: 'Emoji',
required: true,
}),
},
async run(configValue) {
const { workspace_id, message_id, emoji } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.POST,
`workspaces/${workspace_id}/chat/messages/${message_id}/reactions`,
getAccessTokenOrThrow(configValue.auth),
{ emoji },
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,52 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const createClickupMessageReply = createAction({
auth: clickupAuth,
name: 'create_message_reply',
description: 'Creates a reply to a message in a ClickUp channel',
displayName: 'Create Message Reply',
props: {
workspace_id: clickupCommon.workspace_id(),
message_id: Property.ShortText({
description: 'ID of the message to reply to',
displayName: 'Message ID',
required: true,
}),
content: Property.LongText({
description: 'Content of the message',
displayName: 'Message Content',
required: true,
}),
type: Property.StaticDropdown({
description: 'Type of the message',
displayName: 'Message Type',
required: true,
options: {
options: [
{ label: 'Message', value: 'message' },
{ label: 'Post', value: 'post' },
],
},
defaultValue: 'message',
}),
},
async run(configValue) {
const { workspace_id, message_id, content, type } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.POST,
`workspaces/${workspace_id}/chat/messages/${message_id}/replies`,
getAccessTokenOrThrow(configValue.auth),
{
content,
type,
},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,48 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const createClickupMessage = createAction({
auth: clickupAuth,
name: 'create_message',
description: 'Creates a message in a ClickUp channel',
displayName: 'Create Message',
props: {
workspace_id: clickupCommon.workspace_id(),
channel_id: clickupCommon.channel_id(),
content: Property.LongText({
description: 'Content of the message',
displayName: 'Message Content',
required: true,
}),
type: Property.StaticDropdown({
description: 'Type of the message',
displayName: 'Message Type',
required: true,
options: {
options: [
{ label: 'Message', value: 'message' },
{ label: 'Post', value: 'post' },
],
},
defaultValue: 'message',
}),
},
async run(configValue) {
const { workspace_id, channel_id, content, type } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.POST,
`workspaces/${workspace_id}/chat/channels/${channel_id}/messages`,
getAccessTokenOrThrow(configValue.auth),
{
content,
type,
},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,37 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const deleteClickupMessageReaction = createAction({
auth: clickupAuth,
name: 'delete_message_reaction',
description: 'Deletes a reaction from a message in a ClickUp channel',
displayName: 'Delete Message Reaction',
props: {
workspace_id: clickupCommon.workspace_id(),
message_id: Property.ShortText({
description: 'ID of the message to delete reaction from',
displayName: 'Message ID',
required: true,
}),
reaction_id: Property.ShortText({
description: 'ID of the reaction to delete',
displayName: 'Reaction ID',
required: true,
}),
},
async run(configValue) {
const { workspace_id, message_id, reaction_id } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.DELETE,
`workspaces/${workspace_id}/chat/messages/${message_id}/reactions/${reaction_id}`,
getAccessTokenOrThrow(configValue.auth),
{},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,32 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const deleteClickupMessage = createAction({
auth: clickupAuth,
name: 'delete_message',
description: 'Deletes a message in a ClickUp channel',
displayName: 'Delete Message',
props: {
workspace_id: clickupCommon.workspace_id(),
message_id: Property.ShortText({
description: 'ID of the message to delete',
displayName: 'Message ID',
required: true,
}),
},
async run(configValue) {
const { workspace_id, message_id } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.DELETE,
`workspaces/${workspace_id}/chat/messages/${message_id}`,
getAccessTokenOrThrow(configValue.auth),
{},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,64 @@
import { Property } from '@activepieces/pieces-framework';
import {
HttpMethod,
getAccessTokenOrThrow,
propsValidation,
} from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
import { z } from 'zod';
import { createAction } from '@activepieces/pieces-framework';
export const getClickupChannelMessages = createAction({
auth: clickupAuth,
name: 'get_channel_messages',
description: 'Gets all messages in a ClickUp channel',
displayName: 'Get Channel Messages',
props: {
workspace_id: clickupCommon.workspace_id(),
channel_id: clickupCommon.channel_id(),
limit: Property.Number({
description: 'Limit the number of messages returned',
displayName: 'Limit',
required: false,
defaultValue: 50,
}),
content_format: Property.StaticDropdown({
description: 'Format the content of the messages',
displayName: 'Format Content',
required: false,
options: {
options: [
{ label: 'Markdown', value: 'text/md' },
{ label: 'Plain Text', value: 'text/plain' },
],
},
defaultValue: 'text/md',
}),
},
async run(configValue) {
await propsValidation.validateZod(configValue.propsValue, {
limit: z
.number()
.min(0)
.max(100, 'You can fetch between 1 and 100 messages'),
});
const { workspace_id, channel_id, limit, content_format } =
configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.GET,
`workspaces/${workspace_id}/chat/channels/${channel_id}/messages`,
getAccessTokenOrThrow(configValue.auth),
undefined,
{
limit,
content_format,
}
);
return response.body;
},
});

View File

@@ -0,0 +1,27 @@
import { createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupChannel = createAction({
auth: clickupAuth,
name: 'get_channel',
description: 'Gets a channel in a ClickUp workspace',
displayName: 'Get Channel',
props: {
workspace_id: clickupCommon.workspace_id(),
channel_id: clickupCommon.channel_id(),
},
async run(configValue) {
const { workspace_id, channel_id } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.GET,
`workspaces/${workspace_id}/chat/channels/${channel_id}`,
getAccessTokenOrThrow(configValue.auth),
undefined,
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,56 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
HttpMethod,
getAccessTokenOrThrow,
propsValidation,
} from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
import { z } from 'zod';
export const getClickupChannels = createAction({
auth: clickupAuth,
name: 'get_channels',
description: 'Gets all channels in a ClickUp workspace',
displayName: 'Get Channels',
props: {
workspace_id: clickupCommon.workspace_id(),
include_hidden: Property.Checkbox({
description: 'Include hidden channels',
displayName: 'Include Hidden',
required: false,
defaultValue: false,
}),
limit: Property.Number({
description: 'Limit the number of channels returned',
displayName: 'Limit',
required: false,
defaultValue: 50,
}),
},
async run(configValue) {
await propsValidation.validateZod(configValue.propsValue, {
limit: z
.number()
.min(0)
.max(100, 'You can fetch between 1 and 100 messages'),
});
const { workspace_id, include_hidden, limit } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.GET,
`workspaces/${workspace_id}/chat/channels`,
getAccessTokenOrThrow(configValue.auth),
undefined,
{
limit,
is_follower: false,
include_hidden,
}
);
return response.body;
},
});

View File

@@ -0,0 +1,32 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupMessageReactions = createAction({
auth: clickupAuth,
name: 'get_message_reactions',
description: 'Gets the reactions of a message in a ClickUp channel',
displayName: 'Get Message Reactions',
props: {
workspace_id: clickupCommon.workspace_id(),
message_id: Property.ShortText({
description: 'ID of the message to get reactions for',
displayName: 'Message ID',
required: true,
}),
},
async run(configValue) {
const { workspace_id, message_id } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.GET,
`workspaces/${workspace_id}/chat/messages/${message_id}/reactions`,
getAccessTokenOrThrow(configValue.auth),
{},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,32 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupMessageReplies = createAction({
auth: clickupAuth,
name: 'get_message_replies',
description: 'Gets the replies of a message in a ClickUp channel',
displayName: 'Get Message Replies',
props: {
workspace_id: clickupCommon.workspace_id(),
message_id: Property.ShortText({
description: 'ID of the message to get replies for',
displayName: 'Message ID',
required: true,
}),
},
async run(configValue) {
const { workspace_id, message_id } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.GET,
`workspaces/${workspace_id}/chat/messages/${message_id}/replies`,
getAccessTokenOrThrow(configValue.auth),
{},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,51 @@
import { createAction } from '@activepieces/pieces-framework';
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi3, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const updateClickupMessage = createAction({
auth: clickupAuth,
name: 'update_message',
description: 'Updates a message in a ClickUp channel',
displayName: 'Update Message',
props: {
workspace_id: clickupCommon.workspace_id(),
message_id: Property.ShortText({
description: 'ID of the message to update',
displayName: 'Message ID',
required: true,
}),
content: Property.LongText({
description: 'Content of the message',
displayName: 'Message Content',
required: true,
}),
content_format: Property.StaticDropdown({
description: 'Format of the message content',
displayName: 'Message Content Format',
required: true,
options: {
options: [
{ label: 'Markdown', value: 'text/md' },
{ label: 'Plain Text', value: 'text/plain' },
],
},
}),
},
async run(configValue) {
const { workspace_id, message_id, content, content_format } = configValue.propsValue;
const response = await callClickUpApi3(
HttpMethod.PATCH,
`workspaces/${workspace_id}/chat/messages/${message_id}`,
getAccessTokenOrThrow(configValue.auth),
{
content,
content_format,
},
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,60 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { clickupCommon, callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const createClickupTaskComment = createAction({
auth: clickupAuth,
name: 'create_task_comments',
description: 'Creates a comment on a task in ClickUp',
displayName: 'Create Task Comment',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
list_id: clickupCommon.list_id(),
task_id: clickupCommon.task_id(),
comment: Property.LongText({
description: 'Comment to make on the task',
displayName: 'Comment',
required: true,
}),
assignee_id: clickupCommon.single_assignee_id(
false,
'Assignee Id',
'ID of assignee for Task Comment'
),
},
async run(configValue) {
const { task_id, comment } = configValue.propsValue;
let assignee_id = configValue.propsValue.assignee_id;
if (!assignee_id) {
const user_request = await callClickUpApi(
HttpMethod.GET,
`/user`,
getAccessTokenOrThrow(configValue.auth),
{}
);
if (user_request.body['user'] === undefined) {
throw 'Please connect to your ClickUp account';
}
assignee_id = user_request.body['user']['id'];
}
const response = await callClickUpApi(
HttpMethod.POST,
`/task/${task_id}/comment`,
getAccessTokenOrThrow(configValue.auth),
{
comment_text: comment,
assignee: assignee_id,
notify_all: true,
}
);
return response.body;
},
});

View File

@@ -0,0 +1,29 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupTaskComments = createAction({
auth: clickupAuth,
name: 'get_task_comments',
description: 'Gets comments from a task in ClickUp',
displayName: 'Get Task Comments',
props: {
task_id: Property.ShortText({
description: 'The ID of the task to get',
displayName: 'Task ID',
required: true,
}),
},
async run(configValue) {
const { task_id } = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.GET,
`/task/${task_id}/comment`,
getAccessTokenOrThrow(configValue.auth),
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,22 @@
import { createAction } from "@activepieces/pieces-framework";
import { getAccessTokenOrThrow } from "@activepieces/pieces-common";
import { clickupCommon, listAccessibleCustomFields } from "../../common"
import { clickupAuth } from "../../../";
export const getClickupAccessibleCustomFields = createAction({
auth: clickupAuth,
name: 'get_accessible_custom_fields',
displayName: 'Get Accessible Custom Fields',
description: 'View the Custom Fields available on tasks in a specific List.',
props: {
workspace_id: clickupCommon.workspace_id(true),
space_id: clickupCommon.space_id(true),
list_id: clickupCommon.list_id(true)
},
async run(configValue) {
const { list_id } = configValue.propsValue;
const auth = getAccessTokenOrThrow(configValue.auth)
return (await listAccessibleCustomFields(auth, list_id as unknown as string)).fields
}
})

View File

@@ -0,0 +1,35 @@
import { Property, createAction } from "@activepieces/pieces-framework";
import { HttpMethod, getAccessTokenOrThrow } from "@activepieces/pieces-common";
import { callClickUpApi, clickupCommon } from "../../common"
import { clickupAuth } from "../../../";
export const setClickupCustomFieldValue = createAction({
auth: clickupAuth,
name: 'set_custom_fields_value',
displayName: 'Set Custom Field Value',
description: 'Add data to a Custom field on a task.',
props: {
workspace_id: clickupCommon.workspace_id(true),
space_id: clickupCommon.space_id(true),
list_id: clickupCommon.list_id(false),
task_id: clickupCommon.task_id(true),
field_id: clickupCommon.field_id(true),
value: Property.LongText({
displayName: "Value",
description: "The new value to be set",
required: true
})
},
async run(configValue) {
const { field_id, task_id, value } = configValue.propsValue;
const response = await callClickUpApi<Record<string, unknown>>(
HttpMethod.POST,
`task/${task_id}/field/${field_id}`,
getAccessTokenOrThrow(configValue.auth),
{ value: value }
);
return response.body
}
})

View File

@@ -0,0 +1,33 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { clickupCommon, callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const createClickupFolderlessList = createAction({
auth: clickupAuth,
name: 'create_folderless_list',
description: 'Create a new folderless list in a ClickUp workspace and space',
displayName: 'Create Folderless List',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
name: Property.ShortText({
description: 'The name of the list to create',
displayName: 'List Name',
required: true,
}),
},
async run(configValue) {
const { space_id, name } = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.POST,
`space/${space_id}/list`,
getAccessTokenOrThrow(configValue.auth),
{
name,
}
);
return response.body;
},
});

View File

@@ -0,0 +1,30 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupList = createAction({
auth: clickupAuth,
name: 'get_list',
description: 'Gets a list in a ClickUp',
displayName: 'Get List',
props: {
list_id: Property.ShortText({
description: 'The id of the list to get',
displayName: 'List ID',
required: true,
}),
},
async run(configValue) {
const { list_id } = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.GET,
`list/${list_id}`,
getAccessTokenOrThrow(configValue.auth),
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,25 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupSpace = createAction({
auth: clickupAuth,
name: 'get_space',
description: 'Gets a space in a ClickUp',
displayName: 'Get Space',
props: {
space_id: clickupCommon.space_id(),
},
async run(configValue) {
const { space_id } = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.GET,
`space/${space_id}`,
getAccessTokenOrThrow(configValue.auth),
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,25 @@
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { clickupCommon, callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
import { createAction } from '@activepieces/pieces-framework';
export const getClickupSpaces = createAction({
auth: clickupAuth,
name: 'get_spaces',
description: 'Gets spaces in a ClickUp workspace',
displayName: 'Get Spaces',
props: {
team_id: clickupCommon.workspace_id(),
},
async run(configValue) {
const { team_id } = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.GET,
`team/${team_id}/space`,
getAccessTokenOrThrow(configValue.auth),
{}
);
return response.body;
},
});

View File

@@ -0,0 +1,213 @@
import {
OAuth2PropertyValue,
Property,
createAction,
} from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { MarkdownVariant } from '@activepieces/shared';
import {
clickupCommon,
callClickUpApi,
listAccessibleCustomFields,
} from '../../common';
import { clickupAuth } from '../../../';
export const createClickupSubtask = createAction({
auth: clickupAuth,
name: 'create_subtask',
description: 'Creates a subtask in ClickUp',
displayName: 'Create Subtask',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
list_id: clickupCommon.list_id(),
task_id: clickupCommon.task_id(),
name: Property.ShortText({
description: 'The name of the subtask to create',
displayName: 'Subtask Name',
required: true,
}),
status_id: clickupCommon.status_id(),
priority_id: clickupCommon.priority_id(),
assignee_id: clickupCommon.assignee_id(
false,
'Assignee Id',
'ID of assignee for Clickup Subtask'
),
description: Property.LongText({
description: 'The description of the subtask to create',
displayName: 'Subtask Description',
required: false,
}),
is_markdown: Property.Checkbox({
description: 'Is the description in markdown format',
displayName: 'Is Markdown',
required: false,
defaultValue: false,
}),
due_date: Property.DateTime({
description: 'The due date of the subtask',
displayName: 'Due Date',
required: false,
}),
due_date_time: Property.Checkbox({
description: 'Whether to include time in the due date',
displayName: 'Due Date Time',
required: false,
defaultValue: false,
}),
start_date: Property.DateTime({
description: 'The start date of the subtask',
displayName: 'Start Date',
required: false,
}),
start_date_time: Property.Checkbox({
description: 'Whether to include time in the start date',
displayName: 'Start Date Time',
required: false,
defaultValue: false,
}),
time_estimate: Property.Number({
description: 'The time estimate for the subtask in milliseconds',
displayName: 'Time Estimate',
required: false,
}),
check_required_custom_fields: Property.Checkbox({
description: 'Re-enable required custom fields validation for the subtask',
displayName: 'Check Required Custom Fields',
required: false,
defaultValue: false,
}),
custom_fields_info: Property.MarkDown({
value: `Select custom fields\n\nFor custom dropdown fields, choose a dropdown value based on the index (in the list, the first option is index 0, second is 1, third is 2, etc.)`,
variant: MarkdownVariant.INFO,
}),
custom_fields: Property.DynamicProperties({
auth: clickupAuth,
displayName: 'Custom Fields',
required: true,
refreshers: ['list_id', 'auth'],
props: async ({ list_id, auth }) => {
if (!list_id || !auth) {
return {};
}
// Ensure `auth` is of the correct type
const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
// Fetch custom fields using clickupCommon
const { fields: customFields } = await listAccessibleCustomFields(
accessToken,
list_id.toString()
);
// Map custom fields to InputPropertyMap
const dynamicProps: Record<string, any> = {};
customFields.forEach((field) => {
dynamicProps[field.id] = Property.ShortText({
displayName: field.name,
description: `Value for the custom field: ${field.name}`,
required: false,
});
});
return dynamicProps;
},
}),
},
async run(configValue) {
const {
list_id,
task_id,
name,
description,
status_id,
priority_id,
assignee_id,
is_markdown,
due_date,
due_date_time,
start_date,
start_date_time,
time_estimate,
check_required_custom_fields,
custom_fields,
} = configValue.propsValue;
type SubtaskData = {
name: string;
parent: string | undefined;
status?: string | undefined | string[];
priority?: number | undefined;
assignees?: number[] | undefined;
markdown_content?: string;
description?: string;
due_date?: number;
due_date_time?: boolean;
start_date?: number;
start_date_time?: boolean;
time_estimate?: number;
check_required_custom_fields?: boolean;
custom_fields?: { id: string; value: any }[];
};
const data: SubtaskData = {
name,
parent: task_id, // Parent task ID for the subtask
status: status_id,
priority: priority_id,
assignees: assignee_id,
};
// Add description or markdown content
if (is_markdown && description) {
data.markdown_content = description;
} else if (description) {
data.description = description;
}
// Convert due_date to integer format and add it
if (due_date) {
data.due_date = new Date(due_date).getTime();
data.due_date_time = due_date_time || false;
}
// Convert start_date to integer format and add it
if (start_date) {
data.start_date = new Date(start_date).getTime();
data.start_date_time = start_date_time || false;
}
// Add time estimate
if (time_estimate) {
data.time_estimate = time_estimate;
}
// Add check_required_custom_fields
if (check_required_custom_fields) {
data.check_required_custom_fields = check_required_custom_fields;
}
// Map custom_fields into the required format
if (custom_fields) {
data.custom_fields = Object.entries(custom_fields).map(
([fieldId, value]) => ({
id: fieldId,
value,
})
);
}
// Make the API request
const response = await callClickUpApi(
HttpMethod.POST,
`list/${list_id}/task`,
getAccessTokenOrThrow(configValue.auth),
data
);
return response.body;
},
});

View File

@@ -0,0 +1,36 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { clickupCommon, callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const createClickupTaskFromTemplate = createAction({
auth: clickupAuth,
name: 'create_task_from_template',
description: 'Create a new task from Template',
displayName: 'Create Task From Template',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
list_id: clickupCommon.list_id(),
template_id: clickupCommon.template_id(true),
name: Property.ShortText({
description: 'The name of the task to create',
displayName: 'Task Name',
required: true,
}),
},
async run(configValue) {
const { list_id, name, template_id } = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.POST,
`list/${list_id}/taskTemplate/${template_id}`,
getAccessTokenOrThrow(configValue.auth),
{
name,
}
);
return response.body;
},
});

View File

@@ -0,0 +1,209 @@
import {
OAuth2PropertyValue,
Property,
createAction,
} from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { MarkdownVariant } from '@activepieces/shared';
import {
clickupCommon,
callClickUpApi,
listAccessibleCustomFields,
} from '../../common';
import { clickupAuth } from '../../../';
export const createClickupTask = createAction({
auth: clickupAuth,
name: 'create_task',
description: 'Create a new task in a ClickUp workspace and list',
displayName: 'Create Task',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
list_id: clickupCommon.list_id(),
name: Property.ShortText({
description: 'The name of task',
displayName: 'Name',
required: true,
}),
status_id: clickupCommon.status_id(),
priority_id: clickupCommon.priority_id(),
assignee_id: clickupCommon.assignee_id(
false,
'Assignee Id',
'ID of assignee for Clickup Task'
),
description: Property.LongText({
description: 'The description of task',
displayName: 'Description',
required: false,
}),
is_markdown: Property.Checkbox({
description: 'Is the description in markdown format',
displayName: 'Is Markdown',
required: false,
defaultValue: false,
}),
due_date: Property.DateTime({
description: 'The due date of the task',
displayName: 'Due Date',
required: false,
}),
due_date_time: Property.Checkbox({
description: 'Whether to include time in the due date',
displayName: 'Due Date Time',
required: false,
defaultValue: false,
}),
start_date: Property.DateTime({
description: 'The start date of the task',
displayName: 'Start Date',
required: false,
}),
start_date_time: Property.Checkbox({
description: 'Whether to include time in the start date',
displayName: 'Start Date Time',
required: false,
defaultValue: false,
}),
time_estimate: Property.Number({
description: 'The time estimate for the task in milliseconds',
displayName: 'Time Estimate',
required: false,
}),
check_required_custom_fields: Property.Checkbox({
description: 'Re-enable required custom fields validation for the task',
displayName: 'Check Required Custom Fields',
required: false,
defaultValue: false,
}),
custom_fields_info: Property.MarkDown({
value: `Select custom fields\n\nFor custom dropdown fields, choose a dropdown value based on the index (in the list, the first option is index 0, second is 1, third is 2, etc.)`,
variant: MarkdownVariant.INFO,
}),
custom_fields: Property.DynamicProperties({
auth: clickupAuth,
displayName: 'Custom Fields',
required: true,
refreshers: ['list_id', 'auth'],
props: async ({ list_id, auth }) => {
if (!list_id || !auth) {
return {};
}
// Ensure `auth` is of the correct type
const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
// Fetch custom fields using clickupCommon
const { fields: customFields } = await listAccessibleCustomFields(
accessToken,
list_id.toString()
);
// Map custom fields to InputPropertyMap
const dynamicProps: Record<string, any> = {};
customFields.forEach((field) => {
dynamicProps[field.id] = Property.ShortText({
displayName: field.name,
description: `Value for the custom field: ${field.name}`,
required: false,
});
});
return dynamicProps;
},
}),
},
async run(configValue) {
const {
list_id,
name,
description,
status_id,
priority_id,
assignee_id,
is_markdown,
due_date,
due_date_time,
start_date,
start_date_time,
time_estimate,
check_required_custom_fields,
custom_fields,
} = configValue.propsValue;
type TaskData = {
name: string;
status?: string | undefined | string[];
priority?: number | undefined;
assignees?: number[] | undefined;
markdown_content?: string;
description?: string;
due_date?: number;
due_date_time?: boolean;
start_date?: number;
start_date_time?: boolean;
time_estimate?: number;
check_required_custom_fields?: boolean;
custom_fields?: { id: string; value: any }[];
};
const data: TaskData = {
name,
status: status_id,
priority: priority_id,
assignees: assignee_id,
};
// Add description or markdown content
if (is_markdown && description) {
data.markdown_content = description;
} else if (description) {
data.description = description;
}
// Convert due_date to integer format and add it
if (due_date) {
data.due_date = new Date(due_date).getTime();
data.due_date_time = due_date_time || false;
}
// Convert start_date to integer format and add it
if (start_date) {
data.start_date = new Date(start_date).getTime();
data.start_date_time = start_date_time || false;
}
// Add time estimate
if (time_estimate) {
data.time_estimate = time_estimate;
}
// Add check_required_custom_fields
if (check_required_custom_fields) {
data.check_required_custom_fields = check_required_custom_fields;
}
// Map custom_fields into the required format
if (custom_fields) {
data.custom_fields = Object.entries(custom_fields).map(
([fieldId, value]) => ({
id: fieldId,
value,
})
);
}
// Make the API request
const response = await callClickUpApi(
HttpMethod.POST,
`list/${list_id}/task`,
getAccessTokenOrThrow(configValue.auth),
data
);
return response.body;
},
});

View File

@@ -0,0 +1,31 @@
import { createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { clickupCommon, callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const deleteClickupTask = createAction({
auth: clickupAuth,
name: 'delete_task',
description: 'Delete a task in a workspace and list',
displayName: 'Delete Task',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
list_id: clickupCommon.list_id(),
task_id: clickupCommon.task_id()
},
async run(configValue) {
const {
task_id,
} = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.DELETE,
`task/${task_id}`,
getAccessTokenOrThrow(configValue.auth),
undefined
);
return response.body;
},
});

View File

@@ -0,0 +1,123 @@
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import {
OAuth2PropertyValue,
Property,
createAction,
} from '@activepieces/pieces-framework';
import qs from 'qs';
import { clickupAuth } from '../../..';
import { callClickUpApi, clickupCommon, listTags } from '../../common';
import { ClickupTask } from '../../common/models';
export const filterClickupWorkspaceTasks = createAction({
auth: clickupAuth,
name: 'list_workspace_tasks',
displayName: 'List Team Tasks',
description:
'Retrieves the tasks that meet specific criteria from a Workspace.',
props: {
workspace_id: clickupCommon.workspace_id(true),
space_id: clickupCommon.space_id(false, true),
folder_id: clickupCommon.folder_id(false, true),
list_id: clickupCommon.list_id(false, true),
assignees: clickupCommon.assignee_id(
false,
'Assignee Id',
'ID of assignee for Clickup Task'
),
tags: Property.MultiSelectDropdown({
auth: clickupAuth,
displayName: 'Tags',
description: 'The tags to filter for',
refreshers: ['space_id', 'workspace_id'],
required: false,
options: async ({ auth, workspace_id, space_id }) => {
if (!auth || !workspace_id || !space_id) {
return {
disabled: true,
placeholder:
'connect your account first and select workspace and space',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
const response = await listTags(accessToken, space_id as string);
return {
disabled: false,
options: response.tags.map((tag) => {
return {
label: tag.name,
value: encodeURIComponent(tag.name),
};
}),
};
},
}),
page: Property.Number({
displayName: 'Page',
description: 'Page to fetch (starts at 0).',
required: false,
defaultValue: 0,
}),
reverse: Property.Checkbox({
displayName: 'Reverse',
description: 'Tasks are displayed in reverse order.',
required: false,
defaultValue: false,
}),
include_closed: Property.Checkbox({
displayName: 'Include Closed',
description:
'Include or exclude closed tasks. By default, they are excluded.',
required: false,
defaultValue: false,
}),
order_by: Property.StaticDropdown({
displayName: 'Order By',
description:
'Order by a particular field. By default, tasks are ordered by created.',
required: false,
options: {
options: [
{ value: 'id', label: 'Id' },
{ value: 'created', label: 'Created at' },
{ value: 'updated', label: 'Last updated' },
{ value: 'due_date', label: 'Due date' },
],
},
}),
},
async run(configValue) {
const { list_id, folder_id, space_id, workspace_id, ...params } =
configValue.propsValue;
const auth = getAccessTokenOrThrow(configValue.auth);
const query: Record<string, unknown> = {
assignees: params.assignees,
tags: params.tags,
page: params.page,
reverse: params.reverse,
include_closed: params.include_closed,
order_by: params.order_by,
};
if (list_id) query['list_ids'] = list_id;
if (folder_id) query['project_ids'] = folder_id;
if (space_id) query['space_ids'] = space_id;
return (
await callClickUpApi<ClickupTask>(
HttpMethod.GET,
`team/${workspace_id}/task?${decodeURIComponent(qs.stringify(query))}`,
auth,
undefined,
undefined,
{
'Content-Type': 'application/json',
}
)
).body;
},
});

View File

@@ -0,0 +1,94 @@
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { Property, createAction } from '@activepieces/pieces-framework';
import dayjs from 'dayjs';
import qs from 'qs';
import { clickupAuth } from '../../..';
import { callClickUpApi, clickupCommon } from '../../common';
import { ClickupTask } from '../../common/models';
export const filterClickupWorkspaceTimeEntries = createAction({
auth: clickupAuth,
name: 'list_workspace_time_entries',
displayName: 'List Time Entries',
description: 'Retrieves time entries filtered by start and end date.',
props: {
workspace_id: clickupCommon.workspace_id(true),
start_date: Property.DateTime({
displayName: 'Start date',
description: '',
required: false,
}),
end_date: Property.DateTime({
displayName: 'End date',
required: false,
}),
space_id: clickupCommon.space_id(false),
folder_id: clickupCommon.folder_id(false),
list_id: clickupCommon.list_id(false),
task_id: clickupCommon.task_id(false, 'Task'),
assignee: clickupCommon.assignee_id(
false,
'Assignee Id',
'ID of assignee for Clickup Task'
),
include_task_tags: Property.Checkbox({
displayName: 'Include task tags',
description:
'Include task tags in the response for time entries associated with tasks.',
required: false,
defaultValue: false,
}),
include_location_names: Property.Checkbox({
displayName: 'Include location names',
description:
'Include the names of the List, Folder, and Space along with the list_id, folder_id, and space_id.',
required: false,
defaultValue: false,
}),
},
async run(context) {
const { task_id, list_id, folder_id, space_id, workspace_id, ...params } =
context.propsValue;
const auth = getAccessTokenOrThrow(context.auth);
const query: Record<string, unknown> = {
assignee: params.assignee?.join(','),
include_task_tags: params.include_task_tags,
include_location_names: params.include_location_names,
};
if (params.start_date)
query['start_date'] = dayjs(params.start_date).valueOf();
if (params.end_date) query['end_date'] = dayjs(params.end_date).valueOf();
if (task_id) {
query['task_id'] = task_id;
} else if (list_id) {
query['list_id'] = list_id;
} else if (folder_id) {
query['project_id'] = folder_id;
} else if (space_id) {
query['space_id'] = space_id;
}
return (
await callClickUpApi<ClickupTask>(
HttpMethod.GET,
`team/${workspace_id}/time_entries?${decodeURIComponent(
qs.stringify(query)
)}`,
auth,
undefined,
undefined,
{
'Content-Type': 'application/json',
}
)
).body;
},
});

View File

@@ -0,0 +1,57 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi, clickupCommon } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupTaskByName = createAction({
auth: clickupAuth,
name: 'get_task_by_name',
description: 'Fetches a task by name in a ClickUp list',
displayName: 'Get Task by Name',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
list_id: clickupCommon.list_id(),
task_name: Property.ShortText({
description: 'The name of the task to find',
displayName: 'Task Name',
required: true,
}),
},
async run(configValue) {
const { list_id, task_name } = configValue.propsValue;
const accessToken = getAccessTokenOrThrow(configValue.auth);
let page = 0;
const pageSize = 100;
let hasMorePages = true;
while (hasMorePages) {
const response = await callClickUpApi(
HttpMethod.GET,
`list/${list_id}/task`,
accessToken,
undefined,
{ page, limit: pageSize }
);
const tasks = response.body.tasks;
if (!tasks || tasks.length === 0) {
hasMorePages = false;
break;
}
const matchingTask = tasks.find((task: any) => task.name === task_name);
if (matchingTask) {
return matchingTask;
}
page++;
}
throw new Error(`Task with name "${task_name}" not found in list "${list_id}".`);
},
});

View File

@@ -0,0 +1,36 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const getClickupTask = createAction({
auth: clickupAuth,
name: 'get_list_task',
description: 'Gets a task in a ClickUp list',
displayName: 'Get Task',
props: {
task_id: Property.ShortText({
description: 'The ID of the task to get',
displayName: 'Task ID',
required: true,
}),
include_subtasks: Property.Checkbox({
description: 'Include subtasks in the response',
displayName: 'Include Subtasks',
required: false,
defaultValue: false,
}),
},
async run(configValue) {
const { task_id } = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.GET,
`task/${task_id}`,
getAccessTokenOrThrow(configValue.auth),
undefined
);
return response.body;
},
});

View File

@@ -0,0 +1,68 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { HttpMethod, getAccessTokenOrThrow } from '@activepieces/pieces-common';
import { clickupCommon, callClickUpApi } from '../../common';
import { clickupAuth } from '../../../';
export const updateClickupTask = createAction({
auth: clickupAuth,
name: 'update_task',
description: 'Update task in a ClickUp workspace and list',
displayName: 'Update Task',
props: {
workspace_id: clickupCommon.workspace_id(),
space_id: clickupCommon.space_id(),
list_id: clickupCommon.list_id(),
task_id: clickupCommon.task_id(),
name: Property.ShortText({
description: 'The name of the task to update',
displayName: 'Task Name',
required: false,
}),
description: Property.LongText({
description: 'The description of the task to update',
displayName: 'Task Description',
required: false,
}),
status_id: clickupCommon.status_id(),
priority_id: clickupCommon.priority_id(),
add_assignee: clickupCommon.assignee_id(
false,
'Add Assignees',
'assignee(s) you want to add for the task'
),
rem_assignee: clickupCommon.assignee_id(
false,
'Remove Assignees',
'assignee(s) you want to remove from the task'
),
},
async run(configValue) {
const {
task_id,
name,
description,
status_id,
priority_id,
add_assignee,
rem_assignee,
} = configValue.propsValue;
const response = await callClickUpApi(
HttpMethod.PUT,
`task/${task_id}`,
getAccessTokenOrThrow(configValue.auth),
{
name: name,
description: description,
status: status_id,
priority: priority_id,
assignees: {
add: add_assignee,
rem: rem_assignee,
},
}
);
return response.body;
},
});

View File

@@ -0,0 +1,36 @@
## How to get a list ID
To obtain a list ID
1. Go to ClickUp application (https://app.clickup.com/) and select into a space and a list within the space
2. In the URL bar, it should look something like this: https://app.clickup.com/2499706/v/l/li/901000754585
3. Go ahead and copy the numbers after the final slash (/)
## How to get a space ID
To obtain a space ID
1. Go to ClickUp application (https://app.clickup.com/) and select into a space
2. In the URL bar, it should look something like this: https://app.clickup.com/2499706/v/l/s/38651852
3. Go ahead and copy the numbers after the final slash (/)
## How to get a Team ID
To obtain a team ID
1. Go to ClickUp application (https://app.clickup.com/)
2. Select your preferred workspace (if you haven't already)
3. In the URL bar, it should look something like this: https://app.clickup.com/2499706/home
4. Copy the numbers just after the app.clickup.com (https://app.clickup.com/[COPY NUMBERS HERE]/home)
---
## Triggers
TRIGGERS
---
## Actions
ACTIONS

View File

@@ -0,0 +1,668 @@
import { Property, OAuth2PropertyValue } from '@activepieces/pieces-framework';
import {
getAccessTokenOrThrow,
HttpMethod,
HttpMessageBody,
HttpResponse,
httpClient,
AuthenticationType,
} from '@activepieces/pieces-common';
import { ClickupTask, ClickupWorkspace } from './models';
import { clickupAuth } from '../..';
export const clickupCommon = {
workspace_id: (required = true) =>
Property.Dropdown({
auth: clickupAuth,
description: 'The ID of the ClickUp workspace',
displayName: 'Workspace',
required,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'connect your account first',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = (
await callClickUpApi<{
teams: {
id: string;
name: string;
}[];
}>(HttpMethod.GET, 'team', accessToken, undefined)
).body;
return {
disabled: false,
options: response.teams.map((workspace) => {
return {
label: workspace.name,
value: workspace.id,
};
}),
};
},
}),
space_id: (required = true, multi = false) => {
const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
return Dropdown({
auth: clickupAuth,
description: 'The ID of the ClickUp space to create the task in',
displayName: 'Space',
required,
refreshers: ['workspace_id'],
options: async ({ auth, workspace_id }) => {
if (!auth || !workspace_id) {
return {
disabled: true,
placeholder: 'connect your account first and select workspace',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await listSpaces(accessToken, workspace_id as string);
return {
disabled: false,
options: response.spaces.map((space) => {
return {
label: space.name,
value: space.id,
};
}),
};
},
});
},
list_id: (required = true, multi = false) => {
const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
return Dropdown({
auth: clickupAuth,
description: 'The ID of the ClickUp space to create the task in',
displayName: 'List',
required,
refreshers: ['space_id', 'workspace_id', 'folder_id'],
options: async ({ auth, space_id }) => {
if (!auth || !space_id) {
return {
disabled: true,
placeholder: 'connect your account first and select a space',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const lists: { name: string; id: string }[] = await listAllLists(
accessToken,
space_id as string
);
return {
disabled: false,
options: lists.map((list) => {
return {
label: list.name,
value: list.id,
};
}),
};
},
});
},
task_id: (required = true, label: string | undefined = undefined) =>
Property.Dropdown({
auth: clickupAuth,
description: 'The ID of the ClickUp task',
displayName: label ?? 'Task Id',
required,
defaultValue: null,
refreshers: ['space_id', 'list_id', 'workspace_id', 'folder_id'],
options: async ({ auth, space_id, list_id }) => {
if (!auth || !list_id || !space_id) {
return {
disabled: true,
placeholder:
'connect your account first and select workspace, space and list',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await listTasks(accessToken, list_id as string);
return {
disabled: false,
options: response.tasks.map((task) => {
return {
label: task.name,
value: task.id,
};
}),
};
},
}),
folder_id: (required = false, multi = false) => {
const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
return Dropdown({
auth: clickupAuth,
description: 'The ID of the ClickUp folder',
displayName: 'Folder Id',
refreshers: ['space_id', 'workspace_id'],
required,
options: async ({ auth, space_id, workspace_id }) => {
if (!auth || !workspace_id || !space_id) {
return {
disabled: true,
placeholder:
'connect your account first and select workspace and space',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await listFolders(accessToken, space_id as string);
return {
disabled: false,
options: response.folders.map((task) => {
return {
label: task.name,
value: task.id,
};
}),
};
},
});
},
field_id: (required = false) =>
Property.Dropdown({
auth: clickupAuth,
displayName: 'Field',
description: 'The ID of the ClickUp custom field',
refreshers: ['task_id', 'list_id'],
defaultValue: null,
required,
options: async ({ auth, task_id, list_id }) => {
if (!auth || !task_id || !list_id) {
return {
disabled: true,
placeholder: 'connect your account first and select a task',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await listAccessibleCustomFields(
accessToken,
list_id as string
);
return {
disabled: false,
options: response.fields.map((field) => {
return {
label: field.name,
value: field.id,
};
}),
};
},
}),
status_id: (required = false, multi = false) => {
const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
return Dropdown({
auth: clickupAuth,
description: 'The ID of Clickup Issue Status',
displayName: 'Status Id',
refreshers: ['list_id'],
required,
options: async ({ auth, list_id }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
if (!list_id) {
return {
disabled: true,
placeholder: 'select list',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await getStatuses(accessToken, list_id as string);
return {
disabled: false,
options: response.statuses.map((status) => {
return {
label: status.status,
value: status.status,
};
}),
};
},
});
},
priority_id: (required = false) =>
Property.StaticDropdown({
displayName: 'Priority Id',
defaultValue: null,
description: 'The ID of Clickup Issue Priority',
required,
options: {
options: [
{
label: 'Urgent',
value: 1,
},
{
label: 'High',
value: 2,
},
{
label: 'Normal',
value: 3,
},
{
label: 'Low',
value: 4,
},
],
},
}),
assignee_id: (
required = false,
displayName = 'Assignee Id',
description: string
) =>
Property.MultiSelectDropdown({
auth: clickupAuth,
displayName: displayName,
description: description,
required,
refreshers: ['workspace_id'],
options: async ({ auth, workspace_id }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'connect your account first',
options: [],
};
}
if (!workspace_id) {
return {
disabled: true,
placeholder: 'select workspace',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await listWorkspaceMembers(
accessToken,
workspace_id as string
);
return {
disabled: false,
options: response.map((member) => {
return {
label: member.user.username,
value: member.user.id,
};
}),
};
},
}),
single_assignee_id: (
required = false,
displayName = 'Assignee Id',
description: string
) =>
Property.Dropdown({
auth: clickupAuth,
displayName: displayName,
description: description,
required,
refreshers: ['workspace_id'],
options: async ({ auth, workspace_id }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'connect your account first',
options: [],
};
}
if (!workspace_id) {
return {
disabled: true,
placeholder: 'select workspace',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await listWorkspaceMembers(
accessToken,
workspace_id as string
);
return {
disabled: false,
options: response.map((member) => {
return {
label: member.user.username,
value: member.user.id,
};
}),
};
},
}),
template_id: (required = false) =>
Property.Dropdown({
auth: clickupAuth,
displayName: 'Template Id',
required,
description: 'The ID of Clickup Task Template',
refreshers: ['workspace_id'],
options: async ({ auth, workspace_id }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'connect your account first',
options: [],
};
}
if (!workspace_id) {
return {
disabled: true,
placeholder: 'select workspace',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
const response = await listWorkspaceTemplates(
accessToken,
workspace_id as string
);
return {
disabled: false,
options: response.templates.map((template) => {
return {
label: template.name,
value: template.id,
};
}),
};
},
}),
channel_id: (required = false) =>
Property.Dropdown({
auth: clickupAuth,
displayName: 'Channel Id',
required,
description: 'The ID of Clickup Channel',
refreshers: ['workspace_id'],
options: async ({ auth, workspace_id }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'connect your account first',
options: [],
};
}
if (!workspace_id) {
return {
disabled: true,
placeholder: 'select workspace',
options: [],
};
}
const accessToken = getAccessTokenOrThrow(auth);
const response = await retrieveChannels(
accessToken,
workspace_id as string
);
return {
disabled: false,
options: response.data
.filter((channel) => channel.name && channel.name !== '')
.map((channel) => {
return {
label: channel.name,
value: channel.id,
};
}),
};
},
}),
};
async function listWorkspaces(accessToken: string) {
return (
await callClickUpApi<{ teams: ClickupWorkspace[] }>(
HttpMethod.GET,
'team',
accessToken,
undefined
)
).body;
}
async function getWorkspace(accessToken: string, workspaceId: string) {
const { teams } = await listWorkspaces(accessToken as string);
const workspace = teams.filter((workspace) => workspace.id === workspaceId);
return workspace[0];
}
async function listWorkspaceMembers(accessToken: string, workspaceId: string) {
const workspace = await getWorkspace(accessToken, workspaceId);
return workspace.members;
}
async function listWorkspaceTemplates(
accessToken: string,
workspaceId: string
) {
return (
await callClickUpApi<{
templates: {
id: string;
name: string;
}[];
}>(
HttpMethod.GET,
`team/${workspaceId}/taskTemplate`,
accessToken,
undefined
)
).body;
}
export async function listSpaces(accessToken: string, workspaceId: string) {
return (
await callClickUpApi<{
spaces: {
id: string;
name: string;
}[];
}>(HttpMethod.GET, `team/${workspaceId}/space`, accessToken, undefined)
).body;
}
export async function listAllLists(accessToken: string, spaceId: string) {
const responseFolders = await listFolders(accessToken, spaceId as string);
const promises: Promise<{ lists: { id: string; name: string }[] }>[] = [
listFolderlessList(accessToken, spaceId as string),
];
for (let i = 0; i < responseFolders.folders.length; ++i) {
promises.push(listLists(accessToken, responseFolders.folders[i].id));
}
const listsResponses = await Promise.all(promises);
let lists: { name: string; id: string }[] = [];
for (let i = 0; i < listsResponses.length; ++i) {
lists = [...lists, ...listsResponses[i].lists];
}
return lists;
}
export async function listLists(accessToken: string, folderId: string) {
return (
await callClickUpApi<{
lists: {
id: string;
name: string;
}[];
}>(HttpMethod.GET, `folder/${folderId}/list`, accessToken, undefined)
).body;
}
async function getStatuses(accessToken: string, listId: string) {
return (
await callClickUpApi<{
statuses: {
id: string;
status: string;
}[];
}>(HttpMethod.GET, `list/${listId}`, accessToken, undefined)
).body;
}
export async function listFolders(accessToken: string, spaceId: string) {
return (
await callClickUpApi<{
folders: {
id: string;
name: string;
}[];
}>(HttpMethod.GET, `space/${spaceId}/folder`, accessToken, undefined)
).body;
}
export async function listAccessibleCustomFields(
accessToken: string,
listId: string
) {
return (
await callClickUpApi<{
fields: {
id: string;
name: string;
type: string;
type_config: Record<string, unknown>;
date_created: string;
hide_from_guests: false;
}[];
}>(HttpMethod.GET, `list/${listId}/field`, accessToken, undefined)
).body;
}
async function listFolderlessList(accessToken: string, spaceId: string) {
return (
await callClickUpApi<{
lists: {
id: string;
name: string;
}[];
}>(HttpMethod.GET, `space/${spaceId}/list`, accessToken, undefined)
).body;
}
export async function listTags(accessToken: string, spaceId: string) {
return (
await callClickUpApi<{
tags: {
id: string;
name: string;
}[];
}>(HttpMethod.GET, `space/${spaceId}/tag`, accessToken, undefined)
).body;
}
export async function listTasks(accessToken: string, listId: string) {
return (
await callClickUpApi<{
tasks: {
id: string;
name: string;
}[];
}>(HttpMethod.GET, `list/${listId}/task`, accessToken, undefined)
).body;
}
export async function retrieveChannels(
accessToken: string,
workspaceId: string
) {
return (
await callClickUpApi3<{
data: {
id: string;
name: string;
}[];
}>(
HttpMethod.GET,
`workspaces/${workspaceId}/chat/channels`,
accessToken,
undefined
)
).body;
}
export async function callClickupGetTask(accessToken: string, taskId: string) {
return (
await callClickUpApi<ClickupTask>(
HttpMethod.GET,
`task/${taskId}`,
accessToken,
undefined
)
).body;
}
export async function callClickUpApi<T extends HttpMessageBody = any>(
method: HttpMethod,
apiUrl: string,
accessToken: string,
body: any | undefined,
queryParams: any | undefined = undefined,
headers: any | undefined = undefined
): Promise<HttpResponse<T>> {
headers = {
accept: 'application/json',
...headers,
};
return await httpClient.sendRequest<T>({
method: method,
url: `https://api.clickup.com/api/v2/${apiUrl}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: accessToken,
},
headers,
body: method === 'GET' ? undefined : body,
queryParams,
});
}
export async function callClickUpApi3<T extends HttpMessageBody = any>(
method: HttpMethod,
apiUrl: string,
accessToken: string,
body: any | undefined,
queryParams: any | undefined = undefined,
headers: any | undefined = {}
): Promise<HttpResponse<T>> {
headers = {
accept: 'application/json',
...headers,
};
return await httpClient.sendRequest<T>({
method: method,
url: `https://api.clickup.com/api/v3/${apiUrl}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: accessToken,
},
headers,
body: method === 'GET' ? undefined : body,
queryParams,
});
}

View File

@@ -0,0 +1,127 @@
export const enum ClickupEventType {
TASK_CREATED = 'taskCreated',
TASK_UPDATED = 'taskUpdated',
TASK_DELETED = 'taskDeleted',
TASK_PRIORITY_UPDATED = 'taskPriorityUpdated',
TASK_STATUS_UPDATED = 'taskStatusUpdated',
TASK_ASSIGNEE_UPDATED = 'taskAssigneeUpdated',
TASK_DUEDATE_UPDATED = 'taskDueDateUpdated',
TASK_TAG_UPDATED = 'taskTagUpdated',
TASK_MOVED = 'taskMoved',
TASK_COMMENT_POSTED = 'taskCommentPosted',
TASK_COMMENT_UPDATED = 'taskCommentUpdated',
TASK_TIME_ESTIMATE_UPDATED = 'taskTimeEstimateUpdated',
TASK_TIME_TRACKED_UPDATED = 'taskTimeTrackedUpdated',
LIST_CREATED = 'listCreated',
LIST_UPDATED = 'listUpdated',
LIST_DELETED = 'listDeleted',
FOLDER_CREATED = 'folderCreated',
FOLDER_UPDATED = 'folderUpdated',
FOLDER_DELETED = 'folderDeleted',
SPACE_CREATED = 'spaceCreated',
SPACE_UPDATED = 'spaceUpdated',
SPACE_DELETED = 'spaceDeleted',
GOAL_CREATED = 'goalCreated',
GOAL_UPDATED = 'goalUpdated',
GOAL_DELETED = 'goalDeleted',
KEY_RESULT_CREATED = 'keyResultCreated',
KEY_RESULT_UPDATED = 'keyResultUpdated',
KEY_RESULT_DELETED = 'keyResultDeleted',
AUTOMATION_CREATED = 'automationCreated',
}
export interface ClickupTask {
id: string;
custom_id: string;
name: string;
text_content: string;
description: string;
status: {
status: string;
color: string;
orderindex: number;
type: string;
};
orderindex: string;
date_created: string;
date_updated: string;
date_closed: string;
creator: {
id: number;
username: string;
color: string;
profilePicture: string;
};
assignees: string[];
checklists: string[];
tags: string[];
parent: string;
priority: string;
due_date: string;
start_date: string;
time_estimate: string;
time_spent: string;
custom_fields: Record<string, unknown>[];
list: {
id: string;
};
folder: {
id: string;
};
space: {
id: string;
};
url: string;
}
export interface ClickupWebhookPayload {
event: ClickupEventType;
history_items: {
id: string;
type: number;
date: string;
field: string;
parent_id: string;
data: Record<string, unknown>;
source: string;
user: ClickupUser;
before: string;
after: string;
comment: ClickupComment;
};
task_id: string;
webhook_id: string;
}
interface ClickupUser {
id: number;
username: string;
initials: string;
email: string;
color: string;
profilePicture: string;
}
interface ClickupComment {
id: string;
comment: {
text: string;
}[];
comment_text: string;
user: ClickupUser;
resolved: boolean;
assignee: ClickupUser;
assigned_by: ClickupUser;
reactions: [];
date: string;
}
export interface ClickupWorkspace {
id: string;
name: string;
color: string;
avatar: string;
members: {
user: ClickupUser;
invited_by?: Record<string, unknown>;
}[];
}

View File

@@ -0,0 +1,965 @@
import { ClickupEventType } from '../common/models';
import { clickupRegisterTrigger } from './register-trigger';
import { triggerTaskTagUpdated } from './task-tag-updated';
export const sampleTask = {
id: 'string',
custom_id: 'string',
name: 'string',
text_content: 'string',
description: 'string',
status: {
status: 'in progress',
color: '#d3d3d3',
orderindex: 1,
type: 'custom',
},
orderindex: 'string',
date_created: 'string',
date_updated: 'string',
date_closed: 'string',
creator: {
id: 183,
username: 'John Doe',
color: '#827718',
profilePicture:
'https://attachments-public.clickup.com/profilePictures/183_abc.jpg',
},
assignees: ['string'],
checklists: ['string'],
tags: ['string'],
parent: 'string',
priority: 'string',
due_date: 'string',
start_date: 'string',
time_estimate: 'string',
time_spent: 'string',
custom_fields: [
{
id: 'string',
name: 'string',
type: 'string',
type_config: {},
date_created: 'string',
hide_from_guests: true,
value: {
id: 183,
username: 'John Doe',
email: 'john@example.com',
color: '#7b68ee',
initials: 'JD',
profilePicture: null,
},
required: true,
},
],
list: {
id: '123',
},
folder: {
id: '456',
},
space: {
id: '789',
},
url: 'string',
};
export const triggers = [
// task created
{
name: 'task_created',
eventType: ClickupEventType.TASK_CREATED,
displayName: 'Task Created',
description: 'Triggered when a new task is created.',
sampleData: {
event: 'taskCreated',
history_items: [
{
id: '1394258655167106175',
type: 1,
date: '1378109721053',
field: 'status',
parent_id: '900900799744',
data: {},
source: null,
user: {
id: 55053258,
username: 'Activepieces Apps',
email: 'apps@activepieces.com',
color: '#aa2fff',
initials: 'AA',
profilePicture: null,
},
before: null,
after: '90040005586783',
},
],
task: sampleTask,
task_id: '8669p1zvv',
webhook_id: '9b2708b6-87e8-4fff-851a-2ebf0e35130f',
},
},
// task updated
{
name: 'task_updated',
eventType: ClickupEventType.TASK_UPDATED,
displayName: 'Task Updated',
description: 'Triggered when a task is updated.',
sampleData: {
event: 'taskUpdated',
history_items: [
{
id: '3394266113344261722',
type: 1,
date: '1678110165596',
field: 'name',
parent_id: '900900799744',
data: {},
source: null,
user: {
id: 55053258,
username: 'Activepieces Apps',
email: 'apps@activepieces.com',
color: '#aa2fff',
initials: 'AA',
profilePicture: null,
},
before: "Watch over Neriah's",
after: "Watch over Neriah's things",
},
],
task: sampleTask,
task_id: '8669p1zvv',
webhook_id: 'c065bdfd-eb7a-48dc-b25e-0cf5babf5ec8',
},
},
// task deleted
{
name: 'task_deleted',
eventType: ClickupEventType.TASK_DELETED,
displayName: 'Task Deleted',
description: 'Triggered when a task is deleted.',
sampleData: {
event: 'taskDeleted',
task_id: '8669p1zvv',
webhook_id: '78674f1e-ec8e-4302-908f-4190bdcfdf6a',
},
},
// task priority updated
{
name: 'task_priority_updated',
eventType: ClickupEventType.TASK_PRIORITY_UPDATED,
displayName: "Task Priority Updated",
description: 'Triggered when a task priority is updated.',
sampleData: {
"event": "taskPriorityUpdated",
"history_items": [
{
"id": "2800773800802162647",
"type": 1,
"date": "1642735267148",
"field": "priority",
"parent_id": "162641062",
"data": {},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": null,
"after": {
"id": "2",
"priority": "high",
"color": "#ffcc00",
"orderindex": "2"
}
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// task status updated
{
name: 'task_status_updated',
eventType: ClickupEventType.TASK_STATUS_UPDATED,
displayName: 'Task Status Updated',
description: 'Triggered when a task status is updated.',
sampleData: {
"event": "taskStatusUpdated",
"history_items": [
{
"id": "2800787326392370170",
"type": 1,
"date": "1642736073330",
"field": "status",
"parent_id": "162641062",
"data": {
"status_type": "custom"
},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": {
"status": "to do",
"color": "#f9d900",
"orderindex": 0,
"type": "open"
},
"after": {
"status": "in progress",
"color": "#7C4DFF",
"orderindex": 1,
"type": "custom"
}
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// task assignee updated
{
name: 'task_assignee_updated',
eventType: ClickupEventType.TASK_ASSIGNEE_UPDATED,
displayName: 'Task Assignee Updated',
description: 'Triggered when a task assignee is updated.',
sampleData: {
"event": "taskAssigneeUpdated",
"history_items": [
{
"id": "2800789353868594308",
"type": 1,
"date": "1642736194135",
"field": "assignee_add",
"parent_id": "162641062",
"data": {},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"after": {
"id": 184,
"username": "Sam",
"email": "sam@company.com",
"color": "#7b68ee",
"initials": "S",
"profilePicture": null
}
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// task due date updated
{
name: 'task_due_date_updated',
eventType: ClickupEventType.TASK_DUEDATE_UPDATED,
displayName: 'Task Due Date Updated',
description: 'Triggered when a task due date is updated.',
sampleData: {
"event": "taskDueDateUpdated",
"history_items": [
{
"id": "2800792714143635886",
"type": 1,
"date": "1642736394447",
"field": "due_date",
"parent_id": "162641062",
"data": {
"due_date_time": true,
"old_due_date_time": false
},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": "1642701600000",
"after": "1643608800000"
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// task tag updated
{
name: 'task_tag_updated',
eventType: ClickupEventType.TASK_TAG_UPDATED,
displayName: 'Task Tag Updated',
description: 'Triggered when a task tag is updated.',
sampleData: {
"event": "taskTagUpdated",
"history_items": [
{
"id": "2800797048554170804",
"type": 1,
"date": "1642736652800",
"field": "tag",
"parent_id": "162641062",
"data": {},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": null,
"after": [
{
"name": "def",
"tag_fg": "#FF4081",
"tag_bg": "#FF4081",
"creator": 2770032
}
]
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// task moved
{
name: 'task_moved',
eventType: ClickupEventType.TASK_MOVED,
displayName: 'Task Moved',
description: 'Triggered when a task is moved.',
sampleData: {
"event": "taskMoved",
"history_items": [
{
"id": "2800800851630274181",
"type": 1,
"date": "1642736879339",
"field": "section_moved",
"parent_id": "162641285",
"data": {
"mute_notifications": true
},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": {
"id": "162641062",
"name": "Webhook payloads",
"category": {
"id": "96771950",
"name": "hidden",
"hidden": true
},
"project": {
"id": "7002367",
"name": "This is my API Space"
}
},
"after": {
"id": "162641285",
"name": "webhook payloads 2",
"category": {
"id": "96772049",
"name": "hidden",
"hidden": true
},
"project": {
"id": "7002367",
"name": "This is my API Space"
}
}
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// task comment posted
{
name: 'task_comment_posted',
eventType: ClickupEventType.TASK_COMMENT_POSTED,
displayName: 'Task Comment Posted',
description: 'Triggered when a task comment is posted.',
sampleData: {
event: 'taskCommentPosted',
history_items: [
{
id: '3394271120907039255',
type: 1,
date: '1678110464062',
field: 'comment',
parent_id: '900900799744',
data: {},
source: null,
user: {
id: 55053258,
username: 'Activepieces Apps',
email: 'apps@activepieces.com',
color: '#aa2fff',
initials: 'AA',
profilePicture: null,
},
before: null,
after: '90090008088251',
comment: [
{
text: 'asdsada',
attributes: {},
},
],
},
],
task: sampleTask,
task_id: '8669p1zvv',
webhook_id: '6a86ce24-6276-4315-87f7-b580a9264284',
},
},
// task comment updated
{
name: 'task_comment_updated',
eventType: ClickupEventType.TASK_COMMENT_UPDATED,
displayName: 'Task Comment Updated',
description: 'Triggered when a task comment is updated.',
sampleData: {
event: 'taskCommentUpdated',
history_items: [
{
id: '3394271120907039255',
type: 1,
date: '1678110464062',
field: 'comment',
parent_id: '900900799744',
data: {},
source: null,
user: {
id: 55053258,
username: 'Activepieces Apps',
email: 'apps@activepieces.com',
color: '#aa2fff',
initials: 'AA',
profilePicture: null,
},
before: null,
after: '90090008088251',
comment: [
{
text: 'asdsada',
attributes: {},
},
],
},
],
task: sampleTask,
task_id: '8669p1zvv',
webhook_id: '1c14c8af-5509-4d81-ae7f-c0790c0f9683',
},
},
// task time estimate updated
{
name: 'task_time_estimate_updated',
eventType: ClickupEventType.TASK_TIME_ESTIMATE_UPDATED,
displayName: 'Task Time Estimate Updated',
description: 'Triggered when a task time estimate is updated.',
sampleData: {
"event": "taskTimeEstimateUpdated",
"history_items": [
{
"id": "2800808904123520175",
"type": 1,
"date": "1642737359443",
"field": "time_estimate",
"parent_id": "162641285",
"data": {
"time_estimate_string": "1 hour 30 minutes",
"old_time_estimate_string": null,
"rolled_up_time_estimate": 5400000,
"time_estimate": 5400000,
"time_estimates_by_user": [
{
"userid": 2770032,
"user_time_estimate": "5400000",
"user_rollup_time_estimate": "5400000"
}
]
},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": null,
"after": "5400000"
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// task time tracked updated
{
name: 'task_time_tracked_updated',
eventType: ClickupEventType.TASK_TIME_TRACKED_UPDATED,
displayName: 'Task Time Tracked Updated',
description: 'Triggered when a task time tracked is updated.',
sampleData: {
"event": "taskTimeTrackedUpdated",
"history_items": [
{
"id": "2800809188061123931",
"type": 1,
"date": "1642737376354",
"field": "time_spent",
"parent_id": "162641285",
"data": {
"total_time": "900000",
"rollup_time": "900000"
},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": null,
"after": {
"id": "2800809188061119507",
"start": "1642736476215",
"end": "1642737376215",
"time": "900000",
"source": "clickup",
"date_added": "1642737376354"
}
}
],
"task_id": "1vj38vv",
"data": {
"description": "Time Tracking Created",
"interval_id": "2800809188061119507"
},
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// list created
{
name: 'list_created',
eventType: ClickupEventType.LIST_CREATED,
displayName: 'List Created',
description: 'Triggered when a new list is created.',
sampleData: {
event: 'listCreated',
list_id: '900201745120',
webhook_id: 'eda9be69-dbb9-4c59-a3e2-3f871d5d3b9e',
},
},
// list updated
{
name: 'list_updated',
eventType: ClickupEventType.LIST_UPDATED,
displayName: 'List Updated',
description: 'Triggered when a list is updated.',
sampleData: {
"event": "listUpdated",
"history_items": [
{
"id": "8a2f82db-7718-4fdb-9493-4849e67f009d",
"type": 6,
"date": "1642740510345",
"field": "name",
"parent_id": "162641285",
"data": {},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": "webhook payloads 2",
"after": "Webhook payloads round 2"
}
],
"list_id": "162641285",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// list deleted
{
name: 'list_deleted',
eventType: ClickupEventType.LIST_DELETED,
displayName: 'List Deleted',
description: 'Triggered when a list is deleted.',
sampleData: {
"event": "listDeleted",
"list_id": "162641062",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// folder created
{
name: 'folder_created',
eventType: ClickupEventType.FOLDER_CREATED,
displayName: 'Folder Created',
description: 'Triggered when a new folder is created.',
sampleData: {
"event": "folderCreated",
"folder_id": "96772212",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// folder updated
{
name: 'folder_updated',
eventType: ClickupEventType.FOLDER_UPDATED,
displayName: 'Folder Updated',
description: 'Triggered when a folder is updated.',
sampleData: { "event": "folderUpdated",
"folder_id": "96772212",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// folder deleted
{
name: 'folder_deleted',
eventType: ClickupEventType.FOLDER_DELETED,
displayName: 'Folder Deleted',
description: 'Triggered when a folder is deleted.',
sampleData: {
"event": "listDeleted",
"list_id": "162641543",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// space created
{
name: 'space_created',
eventType: ClickupEventType.SPACE_CREATED,
displayName: 'Space Created',
description: 'Triggered when a new space is created.',
sampleData: {
"event": "spaceCreated",
"space_id": "54650507",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// space updated
{
name: 'space_updated',
eventType: ClickupEventType.SPACE_UPDATED,
displayName: 'Space Updated',
description: 'Triggered when a space is updated.',
sampleData: {
"event": "spaceUpdated",
"space_id": "54650507",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// space deleted
{
name: 'space_deleted',
eventType: ClickupEventType.SPACE_DELETED,
displayName: 'Space Deleted',
description: 'Triggered when a space is deleted.',
sampleData: {
"event": "spaceDeleted",
"space_id": "54650507",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
}
},
// automation created
{
name: 'automation_created',
eventType: ClickupEventType.AUTOMATION_CREATED,
displayName: 'Automation Created',
description: 'Triggered when a new automation is created.',
sampleData: {
"id": "b4ced072-ae72-4c70-b898-fea5dd142408:main",
"trigger_id": "6f612d16-9ff7-4db2-a2f6-19528ee3b90c",
"date": "2024-01-11T19:40:17.927Z",
"payload": {
"id": "8687096vn",
"custom_id": "DEF-43",
"custom_item_id": 0,
"name": "task name",
"text_content": "",
"description": "",
"status": {
"id": "p90090203753_p54073272_p54073128_p54071092_p54067915_r9V2QX7O",
"status": "complete",
"color": "#008844",
"orderindex": 1,
"type": "closed"
},
"orderindex": "39906954.00000000000000000000000000000000",
"date_created": "1705002014968",
"date_updated": "1705002016108",
"date_closed": "1705002016108",
"date_done": "1705002016108",
"archived": false,
"creator": {
"id": 26191384,
"username": "John",
"color": "#5f7c8a",
"email": "john@company.com",
"profilePicture": "https://attachments.clickup.com/profilePictures/26191384_HoB.jpg"
},
"assignees": [],
"watchers": [
{
"id": 26191384,
"username": "John",
"color": "#5f7c8a",
"initials": "J",
"email": "john@company.com",
"profilePicture": "https://attachments.clickup.com/profilePictures/26191384_HoB.jpg"
}
],
"checklists": [],
"tags": [],
"parent": null,
"priority": null,
"due_date": null,
"start_date": null,
"points": null,
"time_estimate": null,
"time_spent": 0,
"custom_fields": [
{
"id": "ede917d5-4dbb-46eb-9f7c-5d4f0a652b1f",
"name": "ijjb",
"type": "formula",
"type_config": {
"version": "1.6",
"is_dynamic": false,
"return_types": [
"null"
],
"calculation_state": "ready"
},
"date_created": "1698260411360",
"hide_from_guests": false,
"required": false
},
{
"id": "7d979288-84e1-48b0-abaf-238ecb27e0fa",
"name": "formula 1",
"type": "currency",
"type_config": {
"default": null,
"precision": 2,
"currency_type": "USD"
},
"date_created": "1694298925344",
"hide_from_guests": false,
"required": false
},
{
"id": "89bbdeeb-6724-4ec0-a8a7-c21d944199a7",
"name": "Marketing Task Type",
"type": "drop_down",
"type_config": {
"default": 0,
"placeholder": null,
"options": [
{
"id": "d73a55af-88f5-4161-a948-7341d2bbb045",
"name": "Campaign",
"color": null,
"orderindex": 0
},
{
"id": "0010111d-91da-4cb7-8cc1-d642f90ef194",
"name": "asd",
"color": null,
"orderindex": 1
}
]
},
"date_created": "1698177406311",
"hide_from_guests": false,
"required": false
},
{
"id": "07119fd9-e1eb-4457-bc69-3e5913707ca2",
"name": "files",
"type": "attachment",
"type_config": {},
"date_created": "1700237528128",
"hide_from_guests": false,
"required": false
},
{
"id": "60392065-eb67-40c3-afe2-10f288d0695d",
"name": "new field",
"type": "currency",
"type_config": {
"precision": 2,
"currency_type": "EUR"
},
"date_created": "1696603471462",
"hide_from_guests": true,
"required": false
}
],
"dependencies": [],
"linked_tasks": [],
"locations": [],
"team_id": "36003581",
"url": "https://app.clickup.com/t/8687096vn",
"sharing": {
"public": false,
"public_share_expires_on": null,
"public_fields": [
"assignees",
"priority",
"due_date",
"content",
"comments",
"attachments",
"customFields",
"subtasks",
"tags",
"checklists",
"coverimage"
],
"token": null,
"seo_optimized": false
},
"list": {
"id": "901102008938",
"name": "List",
"access": true
},
"project": {
"id": "90110993233",
"name": "test folder",
"hidden": false,
"access": true
},
"folder": {
"id": "90110993233",
"name": "test folder",
"hidden": false,
"access": true
},
"space": {
"id": "90090203753"
}
}
}
},
// goal created
{
name: 'goal_created',
eventType: ClickupEventType.GOAL_CREATED,
displayName: 'Goal Created',
description: 'Triggered when a new goal is created.',
sampleData: {
"event": "goalCreated",
"goal_id": "a23e5a3d-74b5-44c2-ab53-917ebe85045a",
"webhook_id": "d5eddb2d-db2b-49e9-87d4-bc6cfbe2313b"
}
},
// goal updated
{
name: 'goal_updated',
eventType: ClickupEventType.GOAL_UPDATED,
displayName: 'Goal Updated',
description: 'Triggered when a goal is updated.',
sampleData: {
"event": "goalUpdated",
"goal_id": "a23e5a3d-74b5-44c2-ab53-917ebe85045a",
"webhook_id": "d5eddb2d-db2b-49e9-87d4-bc6cfbe2313b"
}
},
// goal deleted
{
name: 'goal_deleted',
eventType: ClickupEventType.GOAL_DELETED,
displayName: 'Goal Deleted',
description: 'Triggered when a goal is deleted.',
sampleData: {
"event": "goalDeleted",
"goal_id": "a23e5a3d-74b5-44c2-ab53-917ebe85045a",
"webhook_id": "d5eddb2d-db2b-49e9-87d4-bc6cfbe2313b"
}
},
// key result created
{
name: 'key_result_created',
eventType: ClickupEventType.KEY_RESULT_CREATED,
displayName: 'Key Result Created',
description: 'Triggered when a new key result is created.',
sampleData: {
"event": "keyResultCreated",
"goal_id": "a23e5a3d-74b5-44c2-ab53-917ebe85045a",
"key_result_id": "47608e42-ad0e-4934-a39e-950539c77e79",
"webhook_id": "d5eddb2d-db2b-49e9-87d4-bc6cfbe2313b"
}
},
// key result updated
{
name: 'key_result_updated',
eventType: ClickupEventType.KEY_RESULT_UPDATED,
displayName: 'Key Result Updated',
description: 'Triggered when a key result is updated.',
sampleData: {
"event": "keyResultUpdated",
"goal_id": "a23e5a3d-74b5-44c2-ab53-917ebe85045a",
"key_result_id": "47608e42-ad0e-4934-a39e-950539c77e79",
"webhook_id": "d5eddb2d-db2b-49e9-87d4-bc6cfbe2313b"
}
},
// key result deleted
{
name: 'key_result_deleted',
eventType: ClickupEventType.KEY_RESULT_DELETED,
displayName: 'Key Result Deleted',
description: 'Triggered when a key result is deleted.',
sampleData: {
"event": "keyResultDeleted",
"goal_id": "a23e5a3d-74b5-44c2-ab53-917ebe85045a",
"key_result_id": "47608e42-ad0e-4934-a39e-950539c77e79",
"webhook_id": "d5eddb2d-db2b-49e9-87d4-bc6cfbe2313b"
}
},
].map((props) => clickupRegisterTrigger(props));
export const clickupTriggers = [...triggers, triggerTaskTagUpdated];

View File

@@ -0,0 +1,140 @@
import { TriggerStrategy, createTrigger } from '@activepieces/pieces-framework';
import {
httpClient,
HttpRequest,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { callClickupGetTask, clickupCommon } from '../common';
import { ClickupEventType, ClickupWebhookPayload } from '../common/models';
import { clickupAuth } from '../../';
export const clickupRegisterTrigger = ({
name,
displayName,
eventType,
description,
sampleData,
}: {
name: string;
displayName: string;
eventType: ClickupEventType;
description: string;
sampleData: unknown;
}) =>
createTrigger({
auth: clickupAuth,
name: `clickup_trigger_${name}`,
displayName,
description,
props: {
workspace_id: clickupCommon.workspace_id(true),
space_id: clickupCommon.space_id(false), // Optional, depends on workspace
folder_id: clickupCommon.folder_id(false), // Optional, depends on space
list_id: clickupCommon.list_id(false), // Optional, depends on folder or space
task_id: clickupCommon.task_id(false), // Optional, depends on list
},
sampleData,
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const { workspace_id, space_id, folder_id, list_id, task_id } =
context.propsValue;
const requestBody: Record<string, unknown> = {
endpoint: context.webhookUrl,
events: [eventType],
};
// Add scope narrowing properties to the webhook request body
if (space_id) requestBody.space_id = space_id;
if (folder_id) requestBody.folder_id = folder_id;
if (list_id) requestBody.list_id = list_id;
if (task_id) requestBody.task_id = task_id;
const request: HttpRequest = {
method: HttpMethod.POST,
url: `https://api.clickup.com/api/v2/team/${workspace_id}/webhook`,
body: requestBody,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
queryParams: {},
};
const response = await httpClient.sendRequest<WebhookInformation>(
request
);
console.debug(`clickup.${eventType}.onEnable`, response);
await context.store.put<WebhookInformation>(
`clickup_${name}_trigger`,
response.body
);
},
async onDisable(context) {
const webhook = await context.store.get<WebhookInformation>(
`clickup_${name}_trigger`
);
if (webhook != null) {
const request: HttpRequest = {
method: HttpMethod.DELETE,
url: `https://api.clickup.com/api/v2/webhook/${webhook.id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth['access_token'],
},
};
const response = await httpClient.sendRequest(request);
console.debug(`clickup.${eventType}.onDisable`, response);
}
},
async run(context) {
const payload = context.payload.body as ClickupWebhookPayload;
if (
[
ClickupEventType.TASK_CREATED,
ClickupEventType.TASK_UPDATED,
ClickupEventType.TASK_COMMENT_POSTED,
ClickupEventType.TASK_COMMENT_UPDATED,
].includes(eventType)
) {
const enriched = [
{
...payload,
task: await callClickupGetTask(
context.auth['access_token'],
payload.task_id
),
},
];
console.debug('payload enriched', enriched);
return enriched;
}
return [context.payload.body];
},
});
interface WebhookInformation {
id: string;
webhook: {
id: string;
userid: number;
team_id: number;
endpoint: string;
client_id: string;
events: ClickupEventType[];
task_id?: string;
list_id?: string;
folder_id?: string;
space_id?: string;
health: {
status: string;
fail_count: number;
};
secret: string;
};
}

View File

@@ -0,0 +1,128 @@
import {
TriggerStrategy, createTrigger
} from '@activepieces/pieces-framework';
import {
httpClient,
HttpRequest,
HttpMethod,
AuthenticationType,
} from "@activepieces/pieces-common";
import { callClickupGetTask, clickupCommon } from '../common';
import { ClickupEventType, ClickupWebhookPayload } from '../common/models';
import { clickupAuth } from "../../";
export const triggerTaskTagUpdated = createTrigger({
auth: clickupAuth,
name: 'task_tag_updated',
displayName: 'Task Tag Updated',
description: 'Triggered when a tag is added or removed or renamed on a task.',
sampleData: {
"event": "taskTagUpdated",
"history_items": [
{
"id": "2800797048554170804",
"type": 1,
"date": "1642736652800",
"field": "tag",
"parent_id": "162641062",
"data": {},
"source": null,
"user": {
"id": 183,
"username": "John",
"email": "john@company.com",
"color": "#7b68ee",
"initials": "J",
"profilePicture": null
},
"before": null,
"after": [
{
"name": "def",
"tag_fg": "#FF4081",
"tag_bg": "#FF4081",
"creator": 2770032
}
]
}
],
"task_id": "1vj38vv",
"webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204"
},
props: {
workspace_id: clickupCommon.workspace_id(true),
space_id: clickupCommon.space_id(true),
list_id: clickupCommon.list_id(false),
task_id: clickupCommon.task_id(false),
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const { workspace_id } = context.propsValue
const request: HttpRequest = {
method: HttpMethod.POST,
url: `https://api.clickup.com/api/v2/team/${workspace_id}/webhook`,
body: {
endpoint: context.webhookUrl,
events: [ClickupEventType.TASK_TAG_UPDATED]
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token
},
queryParams: {},
}
const response = await httpClient.sendRequest<WebhookInformation>(request);
console.debug(`clickup.${ClickupEventType.TASK_TAG_UPDATED}.onEnable`, response)
await context.store.put<WebhookInformation>(`clickup_task_tag_updated_trigger`, response.body);
},
async onDisable(context) {
const webhook = await context.store.get<WebhookInformation>(`clickup_task_tag_updated_trigger`);
if (webhook != null) {
const request: HttpRequest = {
method: HttpMethod.DELETE,
url: `https://api.clickup.com/api/v2/webhook/${webhook.id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth['access_token'],
},
};
const response = await httpClient.sendRequest(request);
console.debug(`clickup.${ClickupEventType.TASK_TAG_UPDATED}.onDisable`, response)
}
},
async run(context) {
const payload = context.payload.body as ClickupWebhookPayload
return [{
...payload,
task: await callClickupGetTask(
context.auth['access_token'],
payload.task_id
)
}];
}
});
interface WebhookInformation {
id: string
webhook: {
id: string
userid: number
team_id: number
endpoint: string
client_id: string
events: ClickupEventType[]
task_id: string
list_id: string
folder_id: string
space_id: string
health: {
status: string
fail_count: number
},
secret: string
}
}