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,37 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import {
contactsDropdown,
tagsDropdown,
teamsDropdown,
workspacesDropdown,
} from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
export const applyTagToContact = createAction({
auth: clickfunnelsAuth,
name: 'applyTagToContact',
displayName: 'Apply Tag to Contact',
description: 'Apply a tag to a contact if it doesnt already exist.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
contactId: contactsDropdown(['auth', 'workspaceId']),
tagId: tagsDropdown(['auth', 'workspaceId']),
},
async run({auth, propsValue}) {
const payload = {
contacts_applied_tag: {
tag_id: propsValue.tagId
},
};
const response = await clickfunnelsApiService.createAppliedTag(
auth.props,
propsValue.contactId as string,
payload
);
return response;
},
});

View File

@@ -0,0 +1,67 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import {
contactsDropdown,
pipelinesDropdown,
pipelineStagesDropdown,
teamMembershipsDropdown,
teamsDropdown,
workspacesDropdown,
} from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
export const createOpportunity = createAction({
auth: clickfunnelsAuth,
name: 'createOpportunity',
displayName: 'Create Opportunity',
description:
'Create a new opportunity for a contact.',
props: {
name: Property.ShortText({
displayName: 'Opportunity Name',
description: 'The name or title of the opportunity.',
required: true,
}),
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
pipelineId: pipelinesDropdown(['auth', 'workspaceId']),
pipelineStageId: pipelineStagesDropdown(['auth', 'pipelineId']),
contactId: contactsDropdown(['auth', 'workspaceId']),
assigneeId: teamMembershipsDropdown(['auth', 'teamId'], false),
value: Property.Number({
displayName: 'Value',
description:
'The potential value of this opportunity in the default currency of the workspace',
required: false,
}),
closedAt: Property.DateTime({
displayName: 'Close Date',
description: 'The expected close date for the opportunity.',
required: false,
}),
},
async run({ auth, propsValue }) {
const payload = {
name: propsValue.name,
pipelines_stage_id: propsValue.pipelineStageId,
primary_contact_id: propsValue.contactId,
...(propsValue.value && {
value: propsValue.value,
}),
...(propsValue.closedAt && {
closed_at: new Date(propsValue.closedAt as string).toISOString(),
}),
assignee_id: propsValue.assigneeId,
};
const response = await clickfunnelsApiService.createOpportunity(
auth.props,
propsValue.workspaceId as string,
{
sales_opportunity: payload,
}
);
return response;
},
});

View File

@@ -0,0 +1,28 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { contactsDropdown, coursesDropdown, teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
import { clickfunnelsAuth } from '../common/constants';
export const enrollAContactIntoACourse = createAction({
auth: clickfunnelsAuth,
name: 'enrollAContactIntoACourse',
displayName: 'Enroll a Contact Into a Course',
description: 'Create an enrollment for a contact in a course.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
courseId: coursesDropdown(['auth', 'workspaceId']),
contactId: contactsDropdown(['auth', 'workspaceId']),
},
async run({auth, propsValue}) {
const payload = {
courses_enrollment: {
contact_id: propsValue.contactId
},
};
const response = await clickfunnelsApiService.createCourseEnrollment(auth.props, propsValue.courseId as string, payload);
return response;
},
});

View File

@@ -0,0 +1,28 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
appliedTagsDropdown,
contactsDropdown,
teamsDropdown,
workspacesDropdown,
} from '../common/props';
import { clickfunnelsAuth } from '../common/constants';
import { clickfunnelsApiService } from '../common/requests';
export const removeTagFromContact = createAction({
auth: clickfunnelsAuth,
name: 'removeTagFromContact',
displayName: 'Remove Tag From Contact',
description: 'Remove a specific tag from a contact.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
contactId: contactsDropdown(['auth', 'workspaceId']),
tagId: appliedTagsDropdown(['auth', 'contactId']),
},
async run({auth, propsValue}) {
await clickfunnelsApiService.removeAppliedTags(auth.props, propsValue.tagId as string);
return {
message: "Tag removed"
}
},
});

View File

@@ -0,0 +1,29 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
export const searchContacts = createAction({
auth: clickfunnelsAuth,
name: 'searchContacts',
displayName: 'Search Contacts',
description: 'Look up contacts by ID or email',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
searchTerm: Property.ShortText({
displayName: 'Search Query',
description: 'Filter contacts by either email or id',
required: true,
}),
},
async run({ auth, propsValue }) {
const response = await clickfunnelsApiService.fetchContactByEmailSearch(
auth.props,
propsValue.workspaceId as string,
propsValue.searchTerm as string
);
return response;
},
});

View File

@@ -0,0 +1,56 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { multiTagsDropdown, teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
export const updateOrCreateContact = createAction({
auth: clickfunnelsAuth,
name: 'updateOrCreateContact',
displayName: 'Update or Create Contact',
description:
'Searches for a contact by email and updates it, or creates a new one if not found.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
emailAddress: Property.ShortText({
displayName: 'Email',
description:
"The contact's email address. This is used to find and (update/create) the contact.",
required: true,
}),
firstName: Property.ShortText({
displayName: 'First Name',
required: false,
}),
lastName: Property.ShortText({
displayName: 'Last Name',
required: false,
}),
phoneNumber: Property.ShortText({
displayName: 'Phone Number',
required: false,
}),
tagIds: multiTagsDropdown(['auth', 'workspaceId']),
customAttributes: Property.Object({
displayName: 'Custom Attributes',
description: 'A key-value object for custom contact data. Keys that are default properties on the Contact resource or variations of it will result in an error. E.g., first_name, First Name, etc. are not valid inputs.',
required: false,
}),
},
async run({auth, propsValue}) {
const payload = {
contact: {
email_address: propsValue.emailAddress,
first_name: propsValue.firstName,
last_name: propsValue.lastName,
phone_number: propsValue.phoneNumber,
tag_ids: propsValue.tagIds,
custom_attributes: propsValue.customAttributes
},
};
const response = await clickfunnelsApiService.upsertContact(auth.props, propsValue.workspaceId as string, payload);
return response
},
});

View File

@@ -0,0 +1,56 @@
import { PieceAuth, Property } from "@activepieces/pieces-framework";
import { clickfunnelsApiService } from "./requests";
export const CLICKFUNNELS_BASE_URL = (subdomain: string) => `https://${subdomain}.myclickfunnels.com/api/v2`;
export const API_ENDPOINTS = {
ME: '/me',
TEAMS: '/teams',
WORKSPACES: '/workspaces',
PIPELINES: '/sales/pipelines',
CONTACTS: '/contacts',
COURSES: '/courses',
TAGS: '/tags',
};
export type CLICKFUNNELS_APIKEY_AUTH = {
subdomain: string;
apiKey: string;
};
export const clickfunnelsAuth = PieceAuth.CustomAuth({
description: 'Enter your ClickFunnels subdomain and API key.',
required: true,
props: {
subdomain: Property.ShortText({
displayName: 'Subdomain',
description:
'Your ClickFunnels subdomain (e.g., if your URL is https://mycompany.myclickfunnels.com, enter "mycompany").',
required: true,
}),
apiKey: PieceAuth.SecretText({
displayName: 'API Key',
description:
'Your ClickFunnels API key. You can find this in your ClickFunnels account settings.',
required: true,
}),
},
validate: async ({ auth }) => {
try {
await clickfunnelsApiService.fetchCurrentlyLoggedInUser(auth).catch((err) => {
throw new Error("something went wrong. Please check your username and API key and try again.")
})
return {
valid: true,
};
} catch (error) {
return {
valid: false,
error: `Connection failed: ${
error instanceof Error ? error.message : 'Unknown error'
}`,
};
}
},
});

View File

@@ -0,0 +1,474 @@
import { Property } from '@activepieces/pieces-framework';
import { clickfunnelsApiService } from './requests';
import { clickfunnelsAuth } from './constants';
export const teamsDropdown = (refreshers: string[]) =>
Property.Dropdown({
displayName: 'Team',
description: 'Select the team',
required: true,
refreshers,
auth: clickfunnelsAuth,
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
try {
const response = await clickfunnelsApiService.fetchTeams(auth.props);
return {
options: response.map((team: any) => ({
label: team.name,
value: team.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load teams. Please check your authentication.',
};
}
},
});
export const coursesDropdown = (refreshers: string[]) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Course',
description: 'Select a course',
required: true,
refreshers,
options: async ({ auth, workspaceId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!workspaceId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a workspace first',
};
}
try {
const courses = await clickfunnelsApiService.fetchCourses(
auth.props,
workspaceId as string
);
return {
options: courses.map((course: any) => ({
label: course.title,
value: course.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load teams. Please check your authentication.',
};
}
},
});
export const teamMembershipsDropdown = (
refreshers: string[],
required = true
) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Assignee',
description: 'Select an assignee on your team',
required,
refreshers,
options: async ({ auth, teamId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!teamId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a team first',
};
}
try {
const response = await clickfunnelsApiService.fetchTeam(auth.props, teamId as string);
return {
options: [
{ label: 'Leave Unassigned', value: null },
...response.memberships,
].map((membership) => {
if (membership.label) {
return {
label: membership.label,
value: membership.value,
};
}
return {
label: `${membership.user.first_name} ${membership.user.last_name}`,
value: membership.id,
};
}),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load team members. Please check your authentication.',
};
}
},
});
export const workspacesDropdown = (refreshers: string[]) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Workspace',
description: 'Select the workspace',
required: true,
refreshers,
options: async ({ auth, teamId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!teamId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a team first',
};
}
try {
const workspaces = await clickfunnelsApiService.fetchWorkspaces(
auth.props,
teamId as string
);
return {
options: workspaces.map((workspace: any) => ({
label: workspace.name,
value: workspace.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load workspaces. Please check your authentication.',
};
}
},
});
export const pipelinesDropdown = (refreshers: string[]) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Pipeline',
description: 'Select a pipeline',
required: true,
refreshers,
options: async ({ auth, workspaceId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!workspaceId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a workspace first',
};
}
try {
const pipelines = await clickfunnelsApiService.fetchPipelines(
auth.props,
workspaceId as string
);
return {
options: pipelines.map((pipeline: any) => ({
label: pipeline.name,
value: pipeline.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load pipelines. Please check your authentication.',
};
}
},
});
export const pipelineStagesDropdown = (refreshers: string[]) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Pipeline Stage',
description: 'Select a pipeline stage.',
required: true,
refreshers,
options: async ({ auth, pipelineId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!pipelineId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a pipeline first',
};
}
try {
const pipelineStages = await clickfunnelsApiService.fetchPipelineStages(
auth.props,
pipelineId as string
);
return {
options: pipelineStages.map((pipelineStage: any) => ({
label: pipelineStage.name,
value: pipelineStage.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load pipeline stages. Please check your authentication.',
};
}
},
});
export const contactsDropdown = (refreshers: string[]) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Contact',
description: 'Select a contact',
required: true,
refreshers,
options: async ({ auth, workspaceId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!workspaceId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a workspace first',
};
}
try {
const contacts = await clickfunnelsApiService.fetchContacts(
auth.props,
workspaceId as string
);
return {
options: contacts.map((contact: any) => ({
label: (() => {
const firstName = contact.first_name || '';
const lastName = contact.last_name || '';
const fullName = `${firstName} ${lastName}`.trim();
return (
fullName ||
contact.email_address ||
contact.phone_number ||
'Unknown Contact'
);
})(),
value: contact.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load contacts. Please check your authentication.',
};
}
},
});
export const tagsDropdown = (refreshers: string[]) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Tag',
description: 'Select a tag to apply',
required: true,
refreshers,
options: async ({ auth, workspaceId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!workspaceId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a workspace first',
};
}
try {
const tags = await clickfunnelsApiService.fetchTags(auth.props, workspaceId as string);
return {
options: tags.map((tag: any) => ({
label: tag.name,
value: tag.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder: 'Failed to load tags. Please check your authentication.',
};
}
},
});
export const multiTagsDropdown = (refreshers: string[]) =>
Property.MultiSelectDropdown({
auth: clickfunnelsAuth,
displayName: 'Tag',
description: 'Select tags to apply',
required: false,
refreshers,
options: async ({ auth, workspaceId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!workspaceId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a workspace first',
};
}
try {
const tags = await clickfunnelsApiService.fetchTags(auth.props, workspaceId as string);
return {
options: tags.map((tag: any) => ({
label: tag.name,
value: tag.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder: 'Failed to load tags. Please check your authentication.',
};
}
},
});
export const appliedTagsDropdown = (refreshers: string[]) =>
Property.Dropdown({
auth: clickfunnelsAuth,
displayName: 'Tag',
description: 'Select a tag',
required: true,
refreshers,
options: async ({ auth, contactId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your ClickFunnels account first',
};
}
if (!contactId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a contact first',
};
}
try {
const tags = await clickfunnelsApiService.fetchAppliedTags(
auth.props,
contactId as string
);
return {
options: tags.map((tag: any) => ({
label: tag.tag.name,
value: tag.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder: 'Failed to load tags. Please check your authentication.',
};
}
},
});

View File

@@ -0,0 +1,245 @@
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
import {
API_ENDPOINTS,
CLICKFUNNELS_BASE_URL,
CLICKFUNNELS_APIKEY_AUTH,
} from './constants';
async function fireHttpRequest<T>({
method,
auth,
path,
body,
}: {
auth: CLICKFUNNELS_APIKEY_AUTH;
method: HttpMethod;
path: string;
body?: unknown;
}) {
const BASE_URL = CLICKFUNNELS_BASE_URL(auth.subdomain);
return await httpClient
.sendRequest({
method,
url: `${BASE_URL}${path}`,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${auth.apiKey}`,
},
body,
})
.then((res) => res.body);
}
export const clickfunnelsApiService = {
fetchCurrentlyLoggedInUser: async (auth: CLICKFUNNELS_APIKEY_AUTH) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: API_ENDPOINTS.ME,
});
return response;
},
fetchTeams: async (auth: CLICKFUNNELS_APIKEY_AUTH) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: API_ENDPOINTS.TEAMS,
});
return response;
},
fetchTeam: async (auth: CLICKFUNNELS_APIKEY_AUTH, teamId: string) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.TEAMS}/${teamId}`,
});
return response;
},
fetchWorkspaces: async (auth: CLICKFUNNELS_APIKEY_AUTH, teamId: string) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.TEAMS}/${teamId}${API_ENDPOINTS.WORKSPACES}`,
});
return response;
},
fetchContacts: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
workspaceId: string
) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}${API_ENDPOINTS.CONTACTS}`,
});
return response;
},
fetchContactByEmailSearch: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
workspaceId: string,
filterQuery: string
) => {
const isEmail = filterQuery.includes('@');
const filterParam = isEmail
? `filter[email_address]=${encodeURIComponent(filterQuery)}`
: `filter[id]=${encodeURIComponent(filterQuery)}`;
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}${API_ENDPOINTS.CONTACTS}?${filterParam}`,
});
return response;
},
upsertContact: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
workspaceId: string,
payload: any
) => {
const response = await fireHttpRequest({
method: HttpMethod.POST,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}${API_ENDPOINTS.CONTACTS}/upsert`,
body: payload,
});
return response;
},
fetchTags: async (auth: CLICKFUNNELS_APIKEY_AUTH, workspaceId: string) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}${API_ENDPOINTS.CONTACTS}${API_ENDPOINTS.TAGS}`,
});
return response;
},
fetchCourses: async (auth: CLICKFUNNELS_APIKEY_AUTH, workspaceId: string) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}${API_ENDPOINTS.COURSES}`,
});
return response;
},
createCourseEnrollment: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
courseId: string,
payload: any
) => {
const response = await fireHttpRequest({
method: HttpMethod.POST,
auth,
path: `${API_ENDPOINTS.COURSES}/${courseId}/enrollments`,
body: payload
});
return response;
},
fetchAppliedTags: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
contactId: string
) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.CONTACTS}/${contactId}/applied_tags`,
});
return response;
},
removeAppliedTags: async (auth: CLICKFUNNELS_APIKEY_AUTH, tagId: string) => {
const response = await fireHttpRequest({
method: HttpMethod.DELETE,
auth,
path: `${API_ENDPOINTS.CONTACTS}/applied_tags/${tagId}`,
});
return response;
},
fetchPipelines: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
workspaceId: string
) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}${API_ENDPOINTS.PIPELINES}`,
});
return response;
},
fetchPipelineStages: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
pipelineId: string
) => {
const response = await fireHttpRequest({
method: HttpMethod.GET,
auth,
path: `${API_ENDPOINTS.PIPELINES}/${pipelineId}/stages`,
});
return response;
},
createOpportunity: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
workspaceId: string,
payload: any
) => {
const response = await fireHttpRequest({
method: HttpMethod.POST,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}/sales/opportunities`,
body: payload,
});
return response;
},
createAppliedTag: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
contactId: string,
payload: any
) => {
const response = await fireHttpRequest({
method: HttpMethod.POST,
auth,
path: `${API_ENDPOINTS.CONTACTS}/${contactId}/applied_tags`,
body: payload,
});
return response;
},
createWebhook: async (
auth: CLICKFUNNELS_APIKEY_AUTH,
workspaceId: string,
payload: any
) => {
const response = await fireHttpRequest({
method: HttpMethod.POST,
auth,
path: `${API_ENDPOINTS.WORKSPACES}/${workspaceId}/webhooks/outgoing/endpoints`,
body: payload,
});
return response;
},
deleteWebhook: async (auth: CLICKFUNNELS_APIKEY_AUTH, webhookId: string) => {
const response = await fireHttpRequest({
method: HttpMethod.DELETE,
auth,
path: `/webhooks/outgoing/endpoints/${webhookId}`,
});
return response;
},
};

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_contact_completed_course_trigger';
const MODULE_NAME = 'Contact Completed Course';
export const contactCompletedCourse = createTrigger({
auth: clickfunnelsAuth,
name: 'contactCompletedCourse',
displayName: MODULE_NAME,
description: 'Triggers when a contact completes a course.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['courses/enrollment.course_completed'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_contact_identified_trigger';
const MODULE_NAME = 'Contact Identified';
export const contactIdentified = createTrigger({
auth: clickfunnelsAuth,
name: 'contactIdentified',
displayName: MODULE_NAME,
description: 'Triggers when a new contact is identified by email/phone.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['contact.identified'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_contact_submitted_form_trigger';
const MODULE_NAME = 'Contact Submitted Form';
export const contactSubmittedForm = createTrigger({
auth: clickfunnelsAuth,
name: 'contactSubmittedForm',
displayName: MODULE_NAME,
description: 'Triggers each time a contact submits a form (opt-in or order).',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['form_submission.created'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_contact_suspended_from_course_trigger';
const MODULE_NAME = 'Contact Suspended';
export const contactSuspendedFromCourse = createTrigger({
auth: clickfunnelsAuth,
name: 'contactSuspendedFromCourse',
displayName: `${MODULE_NAME} From Course`,
description: 'Triggers when a contact is suspended from a course.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['courses/enrollment.suspended'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_course_enrollment_created_for_contact_trigger';
const MODULE_NAME = 'Course Enrollment Created';
export const courseEnrollmentCreatedForContact = createTrigger({
auth: clickfunnelsAuth,
name: 'courseEnrollmentCreatedForContact',
displayName: `${MODULE_NAME} for contact`,
description: 'Triggers when a course enrollment is created for a contact.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['courses/enrollment.created'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_onetime_order_paid_trigger';
const MODULE_NAME = 'One-Time Order Paid';
export const oneTimeOrderPaid = createTrigger({
auth: clickfunnelsAuth,
name: 'OneTimeOrderPaid',
displayName: MODULE_NAME,
description: 'Triggers when a customer pays a one-time order.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['one-time-order.completed'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_scheduled_appointment_event_created_trigger';
const MODULE_NAME = 'Scheduled Appointment Event';
export const scheduledAppointmentEventCreated = createTrigger({
auth: clickfunnelsAuth,
name: 'scheduledAppointmentEventCreated',
displayName: `${MODULE_NAME} Created`,
description: 'Triggers when a scheduled appointment event is created.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['appointments/scheduled_event.created'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,64 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { clickfunnelsAuth } from '../common/constants';
import { teamsDropdown, workspacesDropdown } from '../common/props';
import { clickfunnelsApiService } from '../common/requests';
const CACHE_KEY = 'clickfunnels_subscription_invoice_paid_trigger';
const MODULE_NAME = 'Subscription Invoice Paid';
export const subscriptionInvoicePaid = createTrigger({
auth: clickfunnelsAuth,
name: 'subscriptionInvoicePaid',
displayName: MODULE_NAME,
description: 'Triggers when a subscription invoice is paid.',
props: {
teamId: teamsDropdown(['auth']),
workspaceId: workspacesDropdown(['auth', 'teamId']),
},
sampleData: {},
type: TriggerStrategy.WEBHOOK,
async onEnable({ auth, webhookUrl, propsValue: { workspaceId }, store }) {
if (!workspaceId) {
throw new Error('Workspace ID is required. Please select a workspace.');
}
try {
const response: any = await clickfunnelsApiService.createWebhook(
auth.props,
workspaceId as string,
{
webhooks_outgoing_endpoint: {
url: webhookUrl,
name: `ActivePieces ${MODULE_NAME} Webhook - ${Date.now()}`,
event_type_ids: ['subscription.invoice.paid'],
},
}
);
const webhookId = response.id;
await store.put(CACHE_KEY, { webhookId });
} catch (error) {
console.error('Failed to create ClickFunnels webhook:', error);
throw new Error(
`Failed to create webhook: ${
error instanceof Error ? error.message : 'Unknown error'
}`
);
}
},
async onDisable({ auth, store }) {
const cachedData = (await store.get(CACHE_KEY)) as any;
if (cachedData) {
await clickfunnelsApiService
.deleteWebhook(auth.props, cachedData.webhookId)
.then(async () => {
await store.delete(CACHE_KEY);
});
}
},
async run(context) {
return [context.payload.body];
},
});