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,375 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
export const addOrUpdateContact = createAction({
name: 'addOrUpdateContact',
displayName: 'Add or Update Contact',
description: 'Creates a new contact.',
auth: sperseAuth,
props: {
importType: Property.StaticDropdown({
displayName: 'Contact Type',
required: true,
defaultValue: 'Lead',
options: {
disabled: false,
options: [
{
label: 'Lead',
value: 'Lead',
},
{
label: 'Client',
value: 'Client',
},
{
label: 'Partner',
value: 'Partner',
},
],
},
}),
matchExisting: Property.StaticDropdown({
displayName: 'Match Existing Contact',
description:
'If "Yes", will try to find an existing record using Email and Full Name and update it.',
required: false,
defaultValue: true,
options: {
disabled: false,
options: [
{
label: 'Yes',
value: true,
},
{
label: 'No',
value: false,
},
],
},
}),
contactId: Property.Number({
displayName: 'Contact ID',
description: 'Sperse Contact ID. Will be used for looking a client',
defaultValue: 0,
required: false,
}),
// fullname
fullName: Property.ShortText({
displayName: 'Full Name',
description: "The contact's full name.",
required: false,
}),
namePrefix: Property.ShortText({
displayName: 'Prefix',
description: 'The title used to address the contact.',
required: false,
}),
firstName: Property.ShortText({
displayName: 'First Name',
description: 'Required if Last Name and Company Name fields are empty.',
required: true,
}),
middleName: Property.ShortText({
displayName: 'Middle Name',
description: "The contact's middle name.",
required: false,
}),
lastName: Property.ShortText({
displayName: 'Last Name',
description: 'Required if First Name and Company Name fields are empty.',
required: true,
}),
nickName: Property.ShortText({
displayName: 'Nick Name',
description: "The contact's nick name.",
required: false,
}),
nameSuffix: Property.ShortText({
displayName: 'Suffix',
description: 'Additional information about the contact e.g PhD.',
required: false,
}),
// personal info
gender: Property.StaticDropdown({
displayName: 'Gender',
required: false,
options: {
disabled: false,
options: [
{
label: 'Male',
value: 'Male',
},
{
label: 'Female',
value: 'Female',
},
],
},
}),
dob: Property.ShortText({
displayName: 'Date of Birth',
description: 'Valid date format YYYY-MM-DD or MM-DD-YYYY.',
required: false,
}),
bankCode: Property.ShortText({
displayName: 'Bank Code',
description: "The contact's 4-letter personality code.",
required: false,
}),
ssn: Property.ShortText({
displayName: 'SSN',
description: "The contact's social security number.",
required: false,
}),
// business info
companyName: Property.ShortText({
displayName: 'Company Name',
description:
"Name of the contact's company (This field is mandatory if the First Name and Last Name fields are empty).",
required: false,
}),
jobTitle: Property.ShortText({
displayName: 'Job Title',
description: "The contact's job title.",
required: false,
}),
industry: Property.ShortText({
displayName: 'Industry',
description: "The company's industry.",
required: false,
}),
// email
workEmail1: Property.LongText({
displayName: 'Work Email',
description: "The contact's work email.",
required: false,
}),
email1: Property.LongText({
displayName: 'Personal Email',
description: "The contact's personal email.",
required: false,
}),
email2: Property.LongText({
displayName: 'Other email',
description: "The contact's additional email.",
required: false,
}),
// phone
workPhone1: Property.ShortText({
displayName: 'Work Phone',
description: "The contact's work/primary phone number.",
required: false,
}),
homePhone: Property.ShortText({
displayName: 'Home Phone',
description: "The contact's home phone number.",
required: false,
}),
mobilePhone: Property.ShortText({
displayName: 'Mobile Phone',
description: "The contact's mobile phone number.",
required: false,
}),
// links
webSiteUrl: Property.ShortText({
displayName: 'Website',
description: "The contact's company website URL.",
required: false,
}),
linkedInUrl: Property.ShortText({
displayName: 'LinkedIn',
description: "The contact's LinkedIn profile id.",
required: false,
}),
photoUrl: Property.ShortText({
displayName: 'Photo URL',
description: "The contact's person photo URL.",
required: false,
}),
// Full Address
street: Property.ShortText({
displayName: 'Street',
description:
"The contact's full street address (can include apartment or unit number).",
required: false,
}),
addressLine2: Property.ShortText({
displayName: 'Address 2',
required: false,
}),
city: Property.ShortText({
displayName: 'City',
description: "The contact's city of residence.",
required: false,
}),
stateName: Property.ShortText({
displayName: 'State Name',
description: "The contact's state of residence.",
required: false,
}),
stateId: Property.ShortText({
displayName: 'State Code',
description: "The contact's state code.",
required: false,
}),
zip: Property.ShortText({
displayName: 'Zip Code',
description: "The contact's zip/postal code.",
required: false,
}),
countryName: Property.ShortText({
displayName: 'Country Name',
description: "The contact's country of residence.",
required: false,
}),
countryId: Property.ShortText({
displayName: 'Country Code',
description: "The contact's country code.",
required: false,
}),
// content
experience: Property.LongText({
displayName: 'Content',
description: "The contact's professional experience.",
required: false,
}),
profileSummary: Property.LongText({
displayName: 'Profile Summary',
description: "The contact's profile summary.",
required: false,
}),
notes: Property.LongText({
displayName: 'notes',
description: 'Additional notes about the contact',
required: false,
}),
followUpDate: Property.ShortText({
displayName: 'Follow Up Date',
description:
'Valid date format YYYY-MM-DD HH:MM:SS. If date is defined then new Follow Up Task will be created for this contact',
required: false,
}),
assignedUser: Property.ShortText({
displayName: 'Assigned User',
description:
'Preferably, Sperse User Email should be passed as it is unique within Sperse account but User Name can be also passed',
required: false,
}),
leadDealAmount: Property.Number({
displayName: 'Deal Amount',
description: 'Estimated deal/opportunity amount.',
required: false,
}),
// tracking info
leadSource: Property.ShortText({
displayName: 'Source Code',
description:
'The first known source the contact used to find your website. You can set this automatically and update manually later.',
required: false,
}),
channelId: Property.ShortText({
displayName: 'Channel Code',
description: 'The channel/medium the contact used to find your website.',
required: false,
}),
affiliateCode: Property.ShortText({
displayName: 'Affiliate Code',
description:
'The affiliate/referer partner through which the contact signed up.',
required: false,
}),
refererURL: Property.ShortText({
displayName: 'Referer URL',
description:
'The webpage where the contact clicked a link that sent them to your website.',
required: false,
}),
entryUrl: Property.ShortText({
displayName: 'Entry URL',
description:
'The first page of visit through which the contact visited your website.',
required: false,
}),
},
async run(context) {
const contact = {
importType: context.propsValue.importType,
matchExisting: context.propsValue.matchExisting,
contactId: context.propsValue.contactId,
personalInfo: {
fullName: {
namePrefix: context.propsValue.namePrefix,
firstName: context.propsValue.firstName,
middleName: context.propsValue.middleName,
lastName: context.propsValue.lastName,
nameSuffix: context.propsValue.nameSuffix,
nickName: context.propsValue.nickName,
},
doB: context.propsValue.dob,
mobilePhone: context.propsValue.mobilePhone,
homePhone: context.propsValue.homePhone,
ssn: context.propsValue.ssn,
bankCode: context.propsValue.bankCode,
email1: context.propsValue.email1,
email2: context.propsValue.email2,
gender: context.propsValue.gender,
fullAddress: {
street: context.propsValue.street,
addressLine2: context.propsValue.addressLine2,
city: context.propsValue.city,
stateName: context.propsValue.stateName,
stateId: context.propsValue.stateId,
zip: context.propsValue.zip,
countryName: context.propsValue.countryName,
countryId: context.propsValue.countryId,
},
webSiteUrl: context.propsValue.webSiteUrl,
linkedInUrl: context.propsValue.linkedInUrl,
photoUrl: context.propsValue.photoUrl,
experience: context.propsValue.experience,
profileSummary: context.propsValue.profileSummary,
},
businessInfo: {
companyName: context.propsValue.companyName,
jobTitle: context.propsValue.jobTitle,
industry: context.propsValue.industry,
workPhone1: context.propsValue.workPhone1,
workEmail1: context.propsValue.workEmail1,
},
assignedUser: context.propsValue.assignedUser,
followUpDate: context.propsValue.followUpDate,
notes: context.propsValue.notes,
leadDealAmount: context.propsValue.leadDealAmount,
leadSource: context.propsValue.leadSource,
channelId: context.propsValue.channelId,
affiliateCode: context.propsValue.affiliateCode,
refererUrl: context.propsValue.refererURL,
entryUrl: context.propsValue.entryUrl,
};
const res = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${context.auth.props.base_url}/api/services/CRM/Import/ImportContact`,
headers: {
'api-key': context.auth.props.api_key, // Pass API key in headers
'Content-Type': 'application/json',
},
body: {
...contact,
},
});
return {
status: res.status,
body: res.body,
};
},
});

View File

@@ -0,0 +1,110 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
export const addOrUpdateSubscription = createAction({
name: 'addOrUpdateSubscription',
displayName: 'Add or Update Subscription',
description: 'Creates a new subscription.',
auth: sperseAuth,
props: {
contactId: Property.Number({
displayName: 'Contact ID',
defaultValue: 0,
required: false,
}),
productId: Property.Number({
displayName: 'Product ID',
required: false,
}),
contactXref: Property.ShortText({
displayName: 'External Contact ID',
description:
'ContactXref have to be specified and correct to look up the correct contact',
required: false,
}),
productCode: Property.ShortText({
displayName: 'Product Code',
description:
'Product Code (Unique product identifier). ProductCode have to be specified and correct to look up the correct product',
required: true,
}),
paymentPeriodType: Property.StaticDropdown({
displayName: 'Payment Period Type',
description:
'The chosen Period Type has to be set for the Product on Sperse side',
required: true,
defaultValue: 'Monthly',
options: {
disabled: false,
options: [
{
label: 'Monthly',
value: 'Monthly',
},
{
label: 'Annual',
value: 'Annual',
},
{
label: 'LifeTime',
value: 'LifeTime',
},
],
},
}),
hasRecurringBilling: Property.StaticDropdown({
displayName: 'Is it Recurring Billing',
required: false,
defaultValue: false,
options: {
disabled: false,
options: [
{
label: 'Yes',
value: true,
},
{
label: 'No',
value: false,
},
],
},
}),
},
async run(context) {
const subscription = {
contactId: context.propsValue.contactId,
contactXref: context.propsValue.contactXref,
products: [
{
productId: context.propsValue.productId,
productCode: context.propsValue.productCode,
paymentPeriodType: context.propsValue.paymentPeriodType,
hasRecurringBilling: context.propsValue.hasRecurringBilling,
},
],
productId: context.propsValue.productId,
productCode: context.propsValue.productCode,
paymentPeriodType: context.propsValue.paymentPeriodType,
hasRecurringBilling: context.propsValue.hasRecurringBilling,
};
const res = await httpClient.sendRequest({
method: HttpMethod.PUT,
url: `${context.auth.props.base_url}/api/services/CRM/OrderSubscription/Update`,
headers: {
'api-key': context.auth.props.api_key, // Pass API key in headers
'Content-Type': 'application/json',
},
body: {
...subscription,
},
});
return {
status: res.status,
body: res.body,
};
},
});

View File

@@ -0,0 +1,474 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
// Helper function to get current date in the required format
const getCurrentDateInISOFormat = () => {
return new Date().toISOString(); // Returns current date in format: YYYY-MM-DDTHH:MM:SSZ
};
export const createInvoice = createAction({
name: 'createInvoice',
displayName: 'Create Invoice',
description: 'Creates a new invoice in the CRM.',
auth: sperseAuth,
props: {
contactId: Property.Number({
displayName: 'Contact ID',
description: 'Sperse Contact ID. Will be used for looking a client',
defaultValue: 0,
required: false,
}),
contactXref: Property.ShortText({
displayName: 'External Contact ID',
description:
'External Contact Reference (ID) . Will be used for looking a client',
required: false,
}),
status: Property.StaticDropdown({
displayName: 'Status',
required: true,
defaultValue: 'Draft',
options: {
disabled: false,
options: [
{
label: 'Draft',
value: 'Draft',
},
{
label: 'Final',
value: 'Final',
},
{
label: 'Paid',
value: 'Paid',
},
{
label: 'Sent',
value: 'Sent',
},
],
},
}),
invoiceNo: Property.Number({
displayName: 'Invoice No.',
defaultValue: 0,
required: true,
}),
date: Property.DateTime({
displayName: 'Date of the Invoice',
description: 'should be like this: 2024-06-11T11:11:41Z',
defaultValue: getCurrentDateInISOFormat(),
required: true,
}),
dueDate: Property.DateTime({
displayName: 'Due Date of the Invoice',
description: 'should be like this: 2024-06-11T11:11:41Z',
required: true,
}),
currencyId: Property.StaticDropdown({
displayName: 'Currency Id',
required: true,
defaultValue: 'USD',
options: {
disabled: false,
options: [
{
label: 'USD',
value: 'USD',
},
{
label: 'JPY',
value: 'JPY',
},
{
label: 'IND',
value: 'IND',
},
{
label: 'EUR',
value: 'EUR',
},
{
label: 'GBP',
value: 'GBP',
},
{
label: 'AUD',
value: 'AUD',
},
{
label: 'CAD',
value: 'CAD',
},
{
label: 'CHF',
value: 'CHF',
},
{
label: 'CNY',
value: 'CNY',
},
{
label: 'SEK',
value: 'SEK',
},
{
label: 'NZD',
value: 'NZD',
},
],
},
}),
grandTotal: Property.Number({
displayName: 'Grand Total',
defaultValue: 0,
required: false,
}),
discountTotal: Property.Number({
displayName: 'Discount Total',
defaultValue: 0,
required: false,
}),
shippingTotal: Property.Number({
displayName: 'Shipping Total',
defaultValue: 0,
required: false,
}),
taxTotal: Property.Number({
displayName: 'Tax Total',
defaultValue: 0,
required: false,
}),
// billing
bCompany: Property.ShortText({
displayName: 'Billing Company',
required: false,
}),
bFirstName: Property.ShortText({
displayName: 'First Name',
required: false,
}),
bLastName: Property.ShortText({
displayName: 'Last Name',
required: false,
}),
bPhone: Property.ShortText({
displayName: 'Phone',
required: false,
}),
bEmail: Property.ShortText({
displayName: 'Email',
required: false,
}),
bCountryId: Property.ShortText({
displayName: 'Country Id',
required: false,
}),
bStateId: Property.ShortText({
displayName: 'State Id',
required: false,
}),
bStateName: Property.ShortText({
displayName: 'State Name',
required: false,
}),
bCity: Property.ShortText({
displayName: 'City',
required: false,
}),
bZip: Property.ShortText({
displayName: 'Zip',
required: false,
}),
bAddress1: Property.LongText({
displayName: 'Billing Address 1',
required: false,
}),
bAddress2: Property.LongText({
displayName: 'Billing Address 2',
required: false,
}),
// shipping
sCompany: Property.ShortText({
displayName: 'Shipping Company',
required: false,
}),
sFirstName: Property.ShortText({
displayName: 'First Name',
required: false,
}),
sLastName: Property.ShortText({
displayName: 'Last Name',
required: false,
}),
sPhone: Property.ShortText({
displayName: 'Phone',
required: false,
}),
sEmail: Property.ShortText({
displayName: 'Email',
required: false,
}),
sCountryId: Property.ShortText({
displayName: 'Country Id',
required: false,
}),
sStateId: Property.ShortText({
displayName: 'State Id',
required: false,
}),
sStateName: Property.ShortText({
displayName: 'State Name',
required: false,
}),
sCity: Property.ShortText({
displayName: 'City',
required: false,
}),
sZip: Property.ShortText({
displayName: 'Zip',
required: false,
}),
sAddress1: Property.LongText({
displayName: 'Shipping Address 1',
required: false,
}),
sAddress2: Property.LongText({
displayName: 'Shipping Address 2',
required: false,
}),
//
note: Property.LongText({
displayName: 'Invoice Note',
required: false,
}),
invoiceDescription: Property.LongText({
displayName: 'Invoice Description',
required: true,
}),
// line
quantity: Property.Number({
displayName: 'Quantity',
defaultValue: 0,
required: true,
}),
rate: Property.Number({
displayName: 'Rate',
defaultValue: 0,
required: false,
}),
itemTotal: Property.Number({
displayName: 'Total Item Price',
defaultValue: 0,
required: false,
}),
commissionableAmount: Property.Number({
displayName: 'Commissionable Amount',
defaultValue: 0,
required: false,
}),
unitId: Property.StaticDropdown({
displayName: 'Unit Id',
required: false,
defaultValue: 'Unit',
options: {
disabled: false,
options: [
{
label: 'Unit',
value: 'Unit',
},
{
label: 'Day',
value: 'Day',
},
{
label: 'Month',
value: 'Month',
},
{
label: 'Year',
value: 'Year',
},
{
label: 'Hour',
value: 'Hour',
},
{
label: 'Kilogram',
value: 'Kilogram',
},
{
label: 'Zone',
value: 'Zone',
},
{
label: 'Package',
value: 'Package',
},
{
label: 'Pound',
value: 'Pound',
},
{
label: 'Piece',
value: 'Piece',
},
{
label: 'Feet',
value: 'Feet',
},
{
label: 'Custom',
value: 'Custom',
},
],
},
}),
productCode: Property.ShortText({
displayName: 'Product Code',
description: 'Product Code. We will look up the product',
required: false,
}),
itemDescription: Property.ShortText({
displayName: 'Description',
required: false,
}),
sortOrder: Property.Number({
displayName: 'Sort Order',
defaultValue: 0,
required: false,
}),
// transactions
transactionDate: Property.DateTime({
displayName: 'Transaction Date',
description: 'should be like this: 2024-06-11T11:11:41Z',
defaultValue: getCurrentDateInISOFormat(),
required: true,
}),
transactionDescription: Property.ShortText({
displayName: 'Transaction Description',
required: false,
}),
amount: Property.Number({
displayName: 'Amount',
defaultValue: 0,
required: false,
}),
gatewayName: Property.ShortText({
displayName: 'Gateway Name',
required: false,
}),
gatewayTransactionId: Property.ShortText({
displayName: 'Gateway Transaction Id',
required: false,
}),
historicalData: Property.StaticDropdown({
displayName: 'Historical Data',
description:
'Pass true if this is not actual transaction. Should be False by default',
required: true,
defaultValue: false,
options: {
disabled: false,
options: [
{
label: 'true',
value: true,
},
{
label: 'false',
value: false,
},
],
},
}),
},
async run(context) {
// construct
const invoice = {
contactId: context.propsValue.contactId,
contactXref: context.propsValue.contactXref,
status: context.propsValue.status,
number: context.propsValue.invoiceNo,
date: context.propsValue.date,
dueDate: context.propsValue.dueDate,
currencyId: context.propsValue.currencyId,
grandTotal: context.propsValue.grandTotal,
discountTotal: context.propsValue.discountTotal,
shippingTotal: context.propsValue.shippingTotal,
taxTotal: context.propsValue.taxTotal,
billingAddress: {
countryId: context.propsValue.bCountryId,
stateId: context.propsValue.bStateId,
stateName: context.propsValue.bStateName,
city: context.propsValue.bCity,
zip: context.propsValue.bZip,
address1: context.propsValue.bAddress1,
address2: context.propsValue.bAddress2,
firstName: context.propsValue.bFirstName,
lastName: context.propsValue.bLastName,
company: context.propsValue.bCompany,
email: context.propsValue.bEmail,
phone: context.propsValue.bPhone,
},
shippingAddress: {
countryId: context.propsValue.sCountryId,
stateId: context.propsValue.sStateId,
stateName: context.propsValue.sStateName,
city: context.propsValue.sCity,
zip: context.propsValue.sZip,
address1: context.propsValue.sAddress1,
address2: context.propsValue.sAddress2,
firstName: context.propsValue.sFirstName,
lastName: context.propsValue.sLastName,
company: context.propsValue.sCompany,
email: context.propsValue.sEmail,
phone: context.propsValue.sPhone,
},
description: context.propsValue.invoiceDescription,
note: context.propsValue.note,
lines: [
{
quantity: context.propsValue.quantity,
rate: context.propsValue.rate,
total: context.propsValue.itemTotal,
commissionableAmount: context.propsValue.commissionableAmount,
unitId: context.propsValue.unitId,
productCode: context.propsValue.productCode,
description: context.propsValue.itemDescription,
sortOrder: context.propsValue.sortOrder,
},
],
transactions: [
{
date: context.propsValue.transactionDate,
description: context.propsValue.transactionDescription,
amount: context.propsValue.amount,
gatewayName: context.propsValue.gatewayName,
gatewayTransactionId: context.propsValue.gatewayTransactionId,
},
],
historicalData: context.propsValue.historicalData,
};
const res = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${context.auth.props.base_url}/api/services/CRM/Import/ImportInvoice`,
headers: {
'api-key': context.auth.props.api_key, // Pass API key in headers
'Content-Type': 'application/json',
},
body: {
...invoice,
},
});
return {
status: res.status,
body: res.body,
};
},
});

View File

@@ -0,0 +1,318 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
export const createProduct = createAction({
name: 'createProduct',
displayName: 'Create Product',
description: 'Creates a new product in the CRM',
auth: sperseAuth,
props: {
productType: Property.StaticDropdown({
displayName: 'Product Type',
required: true,
defaultValue: 'General',
options: {
disabled: false,
options: [
{
label: 'General',
value: 'General',
},
{
label: 'Event',
value: 'Event',
},
{
label: 'Subscription',
value: 'Subscription',
},
{
label: 'Digital',
value: 'Digital',
},
],
},
}),
name: Property.ShortText({
displayName: 'Product Name',
required: true,
}),
code: Property.ShortText({
displayName: 'SKU',
required: true,
description: 'Sku is the product code',
}),
description: Property.LongText({
displayName: 'Description',
required: false,
}),
descriptionHTML: Property.LongText({
displayName: 'Description HTML',
required: false,
description: 'Javascript and media tags are not allowed',
}),
logoURL: Property.LongText({
displayName: 'Logo Url',
required: false,
}),
groupName: Property.ShortText({
displayName: 'Group Name',
required: false,
}),
price: Property.Number({
displayName: 'Price',
required: false,
description: 'Required for General , Digital and Event Product Type',
defaultValue: 0,
}),
currencyId: Property.StaticDropdown({
displayName: 'Currency Id',
required: false,
defaultValue: 'USD',
options: {
disabled: false,
options: [
{
label: 'USD',
value: 'USD',
},
{
label: 'JPY',
value: 'JPY',
},
{
label: 'IND',
value: 'IND',
},
{
label: 'EUR',
value: 'EUR',
},
{
label: 'GBP',
value: 'GBP',
},
{
label: 'AUD',
value: 'AUD',
},
{
label: 'CAD',
value: 'CAD',
},
{
label: 'CHF',
value: 'CHF',
},
{
label: 'CNY',
value: 'CNY',
},
{
label: 'SEK',
value: 'SEK',
},
{
label: 'NZD',
value: 'NZD',
},
],
},
}),
unit: Property.StaticDropdown({
displayName: 'Unit',
required: false,
description: 'Required for General and Digital Product Type',
defaultValue: 'Day',
options: {
disabled: false,
options: [
{
label: 'Day',
value: 'Day',
},
{
label: 'Feet',
value: 'Feet',
},
{
label: 'Hour',
value: 'Hour',
},
{
label: 'Kilogram',
value: 'Kilogram',
},
{
label: 'Pound',
value: 'Pound',
},
{
label: 'Month',
value: 'Month',
},
{
label: 'Package',
value: 'Package',
},
{
label: 'Piece',
value: 'Piece',
},
{
label: 'Unit',
value: 'Unit',
},
{
label: 'Year',
value: 'Year',
},
{
label: 'Zone',
value: 'Zone',
},
{
label: 'Custom',
value: 'Custom',
},
],
},
}),
frequency: Property.StaticDropdown({
displayName: 'Payment Cycle',
required: false,
description: 'Required for General and Digital Product Type',
defaultValue: 'Monthly',
options: {
disabled: false,
options: [
{
label: 'Monthly',
value: 'Monthly',
},
{
label: 'Annual',
value: 'Annual',
},
{
label: 'LifeTime',
value: 'LifeTime',
},
{
label: 'OneTime',
value: 'OneTime',
},
{
label: 'Custom',
value: 'Custom',
},
],
},
}),
fee: Property.Number({
displayName: 'Subscription fee',
required: false,
defaultValue: 0,
}),
cycles: Property.Number({
displayName: 'No of cycles',
required: false,
description: 'Required for all except LifeTime or OneTime plan',
defaultValue: 0,
}),
signupFee: Property.Number({
displayName: 'Signup fee',
required: false,
description: 'Required for all except LifeTime or OneTime plan',
defaultValue: 0,
}),
customPeriodType: Property.StaticDropdown({
displayName: 'Custom Period Type',
required: false,
description: 'Required for Custom or OneTime Plan',
defaultValue: 'Days',
options: {
disabled: false,
options: [
{
label: 'Days',
value: 'Days',
},
{
label: 'Weeks',
value: 'Weeks',
},
{
label: 'Months',
value: 'Months',
},
{
label: 'Years',
value: 'Years',
},
],
},
}),
customPeriodCount: Property.Number({
displayName: 'Custom No of Period',
required: false,
description: 'Required for Custom or OneTime Plan',
defaultValue: 0,
}),
trialDayCount: Property.Number({
displayName: 'Custom No of Period',
required: false,
description: 'Required for OneTime plan',
defaultValue: 0,
}),
gracePeriodDayCount: Property.Number({
displayName: 'Grace Period Count',
required: false,
defaultValue: 0,
}),
},
async run(context) {
const product = {
code: context.propsValue.code,
name: context.propsValue.name,
logoUrl: context.propsValue.logoURL,
description: context.propsValue.description,
descriptionHtml: context.propsValue.descriptionHTML,
groupName: context.propsValue.groupName,
type: context.propsValue.productType,
price: context.propsValue.price,
currencyId: context.propsValue.currencyId,
unit: context.propsValue.unit,
productSubscriptionOptions: [
{
frequency: context.propsValue.frequency,
signupFee: context.propsValue.signupFee,
fee: context.propsValue.fee,
trialDayCount: context.propsValue.trialDayCount,
customPeriodCount: context.propsValue.customPeriodCount,
customPeriodType: context.propsValue.customPeriodType,
cycles: context.propsValue.cycles,
gracePeriodDayCount: context.propsValue.gracePeriodDayCount,
},
],
};
const res = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${context.auth.props.base_url}/api/services/CRM/Import/ImportProduct`,
headers: {
'api-key': context.auth.props.api_key, // Pass API key in headers
'Content-Type': 'application/json',
},
body: {
...product,
},
});
return {
status: res.status,
body: res.body,
};
},
});

View File

@@ -0,0 +1,73 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
export const getContactDetails = createAction({
name: 'getContactDetails',
displayName: 'Get Contact Details',
description: 'Get Contact Details',
auth: sperseAuth,
props: {
contactId: Property.Number({
displayName: 'Contact Id',
required: false,
}),
xref: Property.ShortText({
displayName: 'Contact Xref',
required: false,
}),
affiliateCode: Property.ShortText({
displayName: 'Affiliate Code',
required: false,
}),
userId: Property.Number({
displayName: 'User Id',
required: false,
description: 'Id of the logged in user (not contact id)',
}),
userEmail: Property.LongText({
displayName: 'User Email',
required: false,
description: 'Email of the logged in user',
}),
},
async run(context) {
const contact = {
contactId: context.propsValue.contactId,
xref: context.propsValue.xref,
affiliateCode: context.propsValue.affiliateCode,
userId: context.propsValue.userId,
userEmail: context.propsValue.userEmail,
};
// Filter out keys with undefined values
const filteredContact: Record<string, string | number> = Object.fromEntries(
Object.entries(contact).filter(([, value]) => value !== undefined)
) as Record<string, string | number>; // Cast to ensure it's the correct type
// Create query parameters from the filtered contact object
const queryParams = new URLSearchParams(
Object.entries(filteredContact).map(([key, value]) => [
key,
String(value),
])
);
// Send GET request with query parameters in the URL
const res = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${
context.auth.props.base_url
}/api/services/CRM/Contact/GetContactData?${queryParams.toString()}`,
headers: {
'api-key': context.auth.props.api_key,
'Content-Type': 'application/json',
},
});
return {
status: res.status,
body: res.body,
};
},
});

View File

@@ -0,0 +1,46 @@
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
export const sperseCommon = {
subscribeWebhook: async (
eventName: string,
baseUrl: string,
apiKey: string,
webhookUrl: string
) => {
const request = {
method: HttpMethod.POST,
url: `${baseUrl}/api/services/Platform/Event/Subscribe`,
headers: {
'api-key': apiKey,
'Content-Type': 'application/json',
},
body: {
eventName: eventName,
targetUrl: webhookUrl,
},
};
const res = await httpClient.sendRequest(request);
const { id: webhookId } = res.body.result;
return webhookId;
},
unsubscribeWebhook: async (
baseUrl: string,
apiKey: string,
webhookId: number
) => {
const request = {
method: HttpMethod.POST,
url: `${baseUrl}/api/services/Platform/Event/Unsubscribe?id=${webhookId}`,
headers: {
'api-key': apiKey,
'Content-Type': 'application/json',
},
};
return await httpClient.sendRequest(request);
},
};

View File

@@ -0,0 +1,231 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { sperseCommon } from '../common/common';
export const newLead = createTrigger({
auth: sperseAuth,
name: 'new_lead',
displayName: 'New Lead',
description: 'Triggers when a new lead is created',
props: {},
type: TriggerStrategy.WEBHOOK,
sampleData: {
id: 100500,
group: 'P',
personalInfo: {
fullName: {
namePrefix: 'Mr',
firstName: 'John',
middleName: 'G',
lastName: 'Smith',
nameSuffix: 'Jr',
nickName: 'Johnny',
},
doB: '1992-06-30T00:00:00Z',
mobilePhone: '+12057899877',
mobilePhoneExt: '123',
homePhone: '+12057985632',
homePhoneExt: '123',
phone1: null,
phoneExt1: null,
phone2: null,
phoneExt2: null,
preferredToD: 'Anytime',
timeZone: 'Pacific Standard Time',
ssn: '123456456',
bankCode: 'BANK',
email1: 'personalemail1@gmal.com',
email2: 'personalemail2@hotmail.com',
email3: 'personalemail3@yahoo.com',
email4: null,
email5: null,
drivingLicense: 'ASDF4566G',
drivingLicenseState: 'NY',
isActiveMilitaryDuty: true,
gender: 'Male',
fullAddress: {
street: '999-901 Emancipation Ave',
addressLine2: null,
neighborhood: null,
city: 'Houston',
stateName: 'Texas',
stateId: 'TX',
zip: '77003',
countryName: 'United States of America',
countryId: 'US',
startDate: '2018-04-30T00:00:00Z',
isOwner: true,
},
fullAddress2: null,
fullAddress3: null,
isUSCitizen: true,
webSiteUrl: 'www.myprofile.com',
facebookUrl: 'www.fb.com/j.smith',
linkedInUrl: 'https://www.linkedin.com/j.smith',
instagramUrl: 'https://www.instagram.com/j.smith',
twitterUrl: 'https://twitter.com/j.smith',
googlePlusUrl: 'https://googleplus.com/j.smith',
angelListUrl: 'https://angel.co/',
zoomUrl: 'https://zoom.com',
otherLinkUrl: 'https://otherlink.com',
photoUrl: 'https://www.myprofile.com/profile-pictures/myphoto.png',
experience:
'Improvements made to two existing products, designed five completely new products for four customers.',
profileSummary:
'Highly skilled and results-oriented professional with solid academic preparation holding a Juris Doctor degree and extensive experience in intelligence and special operations seeks position in risk management.',
interests: ['World Economy', 'Baseball'],
affiliateCode: 'PA001',
isActive: null,
customFields: {
customField1: null,
customField2: null,
customField3: null,
customField4: null,
customField5: null,
},
},
businessInfo: {
companyName: 'MyCompany LLC',
organizationType: 'Inc',
jobTitle: 'Chief Executive Officer',
isEmployed: true,
employmentStartDate: '2021-12-30T00:00:00Z',
employeeCount: 500,
dateFounded: '2021-06-30T00:00:00Z',
ein: '35-8896524',
annualRevenue: 6000000.0,
industry: 'Sport and Entertainment',
companyPhone: '+12057784563',
companyPhoneExt: '123',
companyFaxNumber: '+12057324598',
companyEmail: 'companyemail1@company.com',
companyFullAddress: {
street: '1500 Canton st',
addressLine2: null,
neighborhood: null,
city: 'Dallas',
stateName: 'Texas',
stateId: 'TX',
zip: '75201',
countryName: 'United States of America',
countryId: 'US',
startDate: '2018-04-30T00:00:00Z',
isOwner: false,
},
companyWebSiteUrl: 'www.mycompany.com',
companyFacebookUrl: 'www.fb.com/mycompany',
companyLinkedInUrl: 'https://www.linkedin.com/mycompany',
companyInstagramUrl: 'https://www.instagram.com/mycompany',
companyTwitterUrl: 'https://twitter.com/mycompany',
companyGooglePlusUrl: 'https://googleplus.com/mycompany',
companyCrunchbaseUrl: 'https://www.crunchbase.com/mycompany',
companyBBBUrl: 'https://www.bbb.org/en/us/overview-of-bbb-ratings',
companyPinterestUrl: 'https://www.pinterest.com/mycompany',
companyDomainUrl: 'https://www.domain.com/mycompany',
companyAlexaUrl: 'https://www.alexa.com/mycompany',
companyOpenCorporatesUrl: 'https://www.opencorporates.com/mycompany',
companyGlassDoorUrl: 'https://www.classdoor.com/mycompany',
companyTrustpilotUrl: 'https://www.trustpilot.com/mycompany',
companyFollowersUrl: 'https://www.followers.com/mycompany',
companyYoutubeUrl: 'https://www.youtube.com/mycompany',
companyYelpUrl: 'https://www.yelp.com/mycompany',
companyRSSUrl: 'https://www.rss.com/mycompany',
companyNavUrl: 'https://www.nav.com/mycompany',
companyAngelListUrl: 'https://www.angelist.com/mycompany',
companyCalendlyUrl: 'https://www.calendly.com/mycompany',
companyZoomUrl: 'https://zoom.com/mycompany',
companyOtherLinkUrl: 'https://www.otherlink.com/mycompany',
companyLogoUrl: 'https://www.mycompany.com/images/companylogo/logo.png',
workPhone1: '+12057412354',
workPhone1Ext: '123',
workPhone2: '+12057741236',
workPhone2Ext: '123',
workEmail1: 'workemail1@company.com',
workEmail2: 'workemail2@company.com',
workEmail3: 'workemail3@company.com',
workFullAddress: {
street: '1502-1702 Strawberry Rd',
addressLine2: null,
neighborhood: null,
city: 'Pasadena',
stateName: 'Texas',
stateId: 'TX',
zip: '77502',
countryName: 'United States of America',
countryId: 'US',
startDate: '2020-05-30T00:00:00Z',
isOwner: false,
},
affiliateCode: 'CA0001',
},
dateCreated: '2022-06-23T13:32:54.9451405Z',
ipAddress: '192.168.0.1',
trackingInfo: {
sourceCode: 'Code',
channelCode: 'ChannelCode',
affiliateCode: '414CODE',
refererUrl: 'http://www.refererurl.com/referpage.html',
entryUrl: 'https://entryurl.com/start-now/?ref=wow',
userAgent:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
clientIp: '192.168.0.1',
},
applicationInfo: {
applicationId: '771XYZ23234G',
applicantId: '771XYZSD',
applicantUserId: 10229,
clickId: '2B69283E-9433-4CE6-A24E-D8809C050B70',
requestedLoanAmount: 89.0,
incomeType: 'Benefits',
netMonthlyIncome: 899.0,
payFrequency: 'Monthly',
payNextDate: '2022-07-30T13:32:54.945358Z',
bankName: 'Bank Name',
bankAccountType: 'Checking',
monthsAtBank: 24,
bankAccountNumber: '26005325777412',
creditScoreRating: 'Excellent',
loanReason: 'AutoPurchase',
creditCardDebtAmount: 56898.0,
},
classificationInfo: {
rating: '10',
lists: ['My List 1', 'My List 2', 'My List 3'],
tags: ['My Tag 1', 'My Tag 2', 'My Tag 3'],
partnerTypeName: 'My Partner Type',
},
eventTime: '2022-06-30T13:32:54Z',
},
async onEnable(context) {
const webhookId = await sperseCommon.subscribeWebhook(
'LeadCreated',
context.auth.props.base_url,
context.auth.props.api_key,
context.webhookUrl
);
await context.store?.put<WebhookInformation>('_new_lead_trigger', {
webhookId: webhookId,
});
},
async onDisable(context) {
const response = await context.store?.get<WebhookInformation>(
'_new_lead_trigger'
);
if (response !== null && response !== undefined) {
await sperseCommon.unsubscribeWebhook(
context.auth.props.base_url,
context.auth.props.api_key,
response.webhookId
);
}
},
async run(context) {
return [context.payload.body];
},
});
interface WebhookInformation {
webhookId: number;
}

View File

@@ -0,0 +1,85 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { sperseCommon } from '../common/common';
export const newPayment = createTrigger({
auth: sperseAuth,
name: 'new_payment',
displayName: 'New Payment',
description: 'Triggers when a new payment is created',
props: {},
type: TriggerStrategy.WEBHOOK,
sampleData: {
contact: {
email: 's@gmail.com',
fullName: 'Test User',
id: 636,
phone: '98877676565',
},
invoice: {
currencyId: 'USD',
date: '2024-06-10T00:00:00Z',
description: 'just a description',
discountTotal: 0,
grandTotal: 100,
id: 457,
number: 'INV - 20240610 - 746C',
shippingTotal: 0,
taxTotal: 0,
lines: [
{
description: 'Me Spacial',
productId: 810,
quantity: 1,
rate: 100,
total: 100,
unitId: 'Month',
},
],
},
transaction: {
amount: 100,
currencyId: 'USD',
date: '2024-06-10T09:36:01.832Z',
gatewayName: null,
gatewayTransactionId: null,
id: 403,
isSuccessful: true,
type: 'Sale',
},
eventTime: '2024-06-10T09:36:08',
eventType: 'Payment.Created',
},
async onEnable(context) {
const webhookId = await sperseCommon.subscribeWebhook(
'Payment.Created',
context.auth.props.base_url,
context.auth.props.api_key,
context.webhookUrl
);
await context.store?.put<WebhookInformation>('_new_payment_trigger', {
webhookId: webhookId,
});
},
async onDisable(context) {
const response = await context.store?.get<WebhookInformation>(
'_new_payment_trigger'
);
if (response !== null && response !== undefined) {
await sperseCommon.unsubscribeWebhook(
context.auth.props.base_url,
context.auth.props.api_key,
response.webhookId
);
}
},
async run(context) {
return [context.payload.body];
},
});
interface WebhookInformation {
webhookId: number;
}

View File

@@ -0,0 +1,61 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { sperseAuth } from '../..';
import { sperseCommon } from '../common/common';
export const newSubscription = createTrigger({
auth: sperseAuth,
name: 'new_subscription',
displayName: 'New Subscription',
description: 'Triggers when a new subscription is created',
props: {},
type: TriggerStrategy.WEBHOOK,
sampleData: {
eventType: 'Subscription.CreatedOrUpdated',
contactId: 3994,
fullname: 'Frank Micheal',
email: 'tray@sperse.com',
id: '536778',
name: 'Subscription_Name',
startDate: '2024-06-30T09:29:43.6271352Z',
endDate: '2025-06-30T09:29:43.6271352Z',
amount: 100,
frequency: 'Annual',
trialDayCount: '4',
gracePeriodCount: '10',
statusId: 'A',
cancelationReason: '',
eventTime: '2024-09-06T00:29:07',
},
async onEnable(context) {
const webhookId = await sperseCommon.subscribeWebhook(
'Subscription.CreatedOrUpdated',
context.auth.props.base_url,
context.auth.props.api_key,
context.webhookUrl
);
await context.store?.put<WebhookInformation>('_new_subscription_trigger', {
webhookId: webhookId,
});
},
async onDisable(context) {
const response = await context.store?.get<WebhookInformation>(
'_new_subscription_trigger'
);
if (response !== null && response !== undefined) {
await sperseCommon.unsubscribeWebhook(
context.auth.props.base_url,
context.auth.props.api_key,
response.webhookId
);
}
},
async run(context) {
return [context.payload.body];
},
});
interface WebhookInformation {
webhookId: number;
}