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,55 @@
import { PieceAuth, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod, AuthenticationType } from '@activepieces/pieces-common';
export type ZendeskSellAuth = {
email: string;
api_token: string;
}
const ZENDESK_SELL_API_URL = 'https://api.getbase.com';
export const zendeskSellAuth = PieceAuth.CustomAuth({
description: `
To get your API token:
1. Log in to your Zendesk Sell account.
2. Go to **Settings > Integrations > APIs**.
3. If no token is active, click **Add API Token**.
4. Copy the **API Token**.
You also need your login email.
`,
required: true,
props: {
email: Property.ShortText({
displayName: 'Email Address',
description: 'Your Zendesk login email address.',
required: true,
}),
api_token: PieceAuth.SecretText({
displayName: 'API Token',
description: 'Your Zendesk Sell API Token.',
required: true,
})
},
validate: async ({ auth }) => {
try {
await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${ZENDESK_SELL_API_URL}/v2/users/self`,
authentication: {
type: AuthenticationType.BASIC,
username: `${auth.email}/token`,
password: auth.api_token,
},
});
return {
valid: true,
};
} catch (e) {
return {
valid: false,
error: 'Invalid API token or email.',
};
}
},
});

View File

@@ -0,0 +1,39 @@
import {
AuthenticationType,
httpClient,
HttpMethod,
HttpRequest,
HttpResponse,
HttpMessageBody,
} from '@activepieces/pieces-common';
import { zendeskSellAuth } from './auth';
import { AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
export const ZENDESK_SELL_API_URL = 'https://api.getbase.com';
export async function callZendeskApi<T>(
method: HttpMethod,
endpoint: string,
auth: AppConnectionValueForAuthProperty<typeof zendeskSellAuth>,
body?: HttpMessageBody,
query?: Record<string, string>
): Promise<HttpResponse<T>> {
const request: HttpRequest = {
method: method,
url: `${ZENDESK_SELL_API_URL}/${endpoint}`,
authentication: {
type: AuthenticationType.BASIC,
username: `${auth.props.email}/token`,
password: auth.props.api_token,
},
body: body,
queryParams: query,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
return httpClient.sendRequest<T>(request);
}

View File

@@ -0,0 +1,245 @@
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { zendeskSellAuth, ZendeskSellAuth } from './auth';
import { callZendeskApi } from './client';
export const zendeskSellCommon = {
lead: (required = true) => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Lead',
required,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, 'v2/leads', auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (error) {
return { disabled: true, placeholder: "Error fetching leads.", options: [] };
}
},
}),
contact: (required = true) => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Contact',
required,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, 'v2/contacts', auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (error) {
return { disabled: true, placeholder: "Error fetching contacts.", options: [] };
}
},
}),
tags: (resourceType: 'contact' | 'lead' | 'deal') => Property.MultiSelectDropdown({
auth: zendeskSellAuth,
displayName: 'Tags',
description: 'A list of tags to associate with the record.',
required: false,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
try {
const response = await callZendeskApi<{ items: { data: { name: string } }[] }>(
HttpMethod.GET, `v2/tags?resource_type=${resourceType}`, auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.name })),
};
} catch (error) {
console.error("Error fetching Zendesk Sell tags:", error);
return { disabled: true, placeholder: "Error fetching tags.", options: [] };
}
},
}),
leadSource: () => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Lead Source',
description: 'The source of the lead.',
required: false,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, 'v2/lead_sources', auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (error) {
console.error("Error fetching Zendesk Sell lead sources:", error);
return { disabled: true, placeholder: "Error fetching lead sources.", options: [] };
}
},
}),
deal: (required = true) => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Deal',
required,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) return { disabled: true, placeholder: 'Connect account first', options: [] };
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, 'v2/deals', auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (e) {
return { disabled: true, placeholder: 'Error fetching deals', options: [] };
}
},
}),
company: (required = true) => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Company',
required,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) return { disabled: true, placeholder: 'Connect account first', options: [] };
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, 'v2/contacts?is_organization=true', auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (e) {
return { disabled: true, placeholder: 'Error fetching companies', options: [] };
}
},
}),
pipeline: (required = true) => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Pipeline',
required,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) return { disabled: true, placeholder: 'Connect account first', options: [] };
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, 'v2/pipelines', auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (e) {
return { disabled: true, placeholder: 'Error fetching pipelines', options: [] };
}
},
}),
stage: (required = true) => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Stage',
required,
refreshers: ['pipeline_id'],
options: async (propsValue) => {
const auth = propsValue.auth
const pipelineId = propsValue['pipeline_id'] as number | undefined;
if (!auth || !pipelineId) {
return { disabled: true, placeholder: 'Select a pipeline first', options: [] };
}
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, `v2/pipelines/${pipelineId}/stages`, auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (e) {
return { disabled: true, placeholder: 'Error fetching stages', options: [] };
}
},
}),
owner: () => Property.Dropdown({
auth: zendeskSellAuth,
displayName: 'Owner',
required: false,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth
if (!auth) return { disabled: true, placeholder: 'Connect account first', options: [] };
try {
const response = await callZendeskApi<{ items: { data: { id: number; name: string } }[] }>(
HttpMethod.GET, 'v2/users', auth
);
return {
disabled: false,
options: response.body.items.map(item => ({ label: item.data.name, value: item.data.id })),
};
} catch (e) {
return { disabled: true, placeholder: 'Error fetching users', options: [] };
}
},
}),
};