first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const campaignOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['campaign'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
action: 'Get a campaign',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
action: 'Get many campaigns',
|
||||
},
|
||||
{
|
||||
name: 'Get Metrics',
|
||||
value: 'getMetrics',
|
||||
action: 'Get metrics for a campaign',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
},
|
||||
];
|
||||
|
||||
export const campaignFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* campaign:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['campaign'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the campaign',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* campaign:getMetrics */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['campaign'],
|
||||
operation: ['getMetrics'],
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Period',
|
||||
name: 'period',
|
||||
type: 'options',
|
||||
default: 'days',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['campaign'],
|
||||
operation: ['getMetrics'],
|
||||
},
|
||||
},
|
||||
description: 'Specify metric period',
|
||||
options: [
|
||||
{
|
||||
name: 'Hours',
|
||||
value: 'hours',
|
||||
},
|
||||
{
|
||||
name: 'Days',
|
||||
value: 'days',
|
||||
},
|
||||
{
|
||||
name: 'Weeks',
|
||||
value: 'weeks',
|
||||
},
|
||||
{
|
||||
name: 'Months',
|
||||
value: 'months',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['campaign'],
|
||||
operation: ['getMetrics'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['campaign'],
|
||||
operation: ['getMetrics'],
|
||||
jsonParameters: [false],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Steps',
|
||||
name: 'steps',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
description:
|
||||
'Integer specifying how many steps to return. Defaults to the maximum number of timeperiods available, or 12 when using the months period. Maximum timeperiods available are 24 hours, 45 days, 12 weeks and 120 months',
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
maxValue: 120,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
default: 'empty',
|
||||
description: 'Specify metric type',
|
||||
options: [
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
},
|
||||
{
|
||||
name: 'Empty',
|
||||
value: 'empty',
|
||||
},
|
||||
{
|
||||
name: 'Push',
|
||||
value: 'push',
|
||||
},
|
||||
{
|
||||
name: 'Slack',
|
||||
value: 'slack',
|
||||
},
|
||||
{
|
||||
name: 'Twilio',
|
||||
value: 'twilio',
|
||||
},
|
||||
{
|
||||
name: 'Urban Airship',
|
||||
value: 'urbanAirship',
|
||||
},
|
||||
{
|
||||
name: 'Webhook',
|
||||
value: 'webhook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,166 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const customerOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['customer'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create or Update',
|
||||
value: 'upsert',
|
||||
description:
|
||||
'Create a new customer, or update the current one if it already exists (upsert)',
|
||||
action: 'Create or update a customer',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a customer',
|
||||
action: 'Delete a customer',
|
||||
},
|
||||
],
|
||||
default: 'upsert',
|
||||
},
|
||||
];
|
||||
|
||||
export const customerFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* customer:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['customer'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* customer:upsert */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['customer'],
|
||||
operation: ['upsert'],
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['customer'],
|
||||
operation: ['upsert'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['customer'],
|
||||
operation: ['upsert'],
|
||||
jsonParameters: [true],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Object of values to set as described <a href="https://github.com/agilecrm/rest-api#1-companys---companies-api">here</a>',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['customer'],
|
||||
operation: ['upsert'],
|
||||
jsonParameters: [false],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Properties',
|
||||
name: 'customProperties',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Property',
|
||||
name: 'customProperty',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Property name',
|
||||
placeholder: 'Plan',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Property value',
|
||||
placeholder: 'Basic',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
placeholder: 'name@email.com',
|
||||
default: '',
|
||||
description: 'The email address of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Created At',
|
||||
name: 'createdAt',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'The UNIX timestamp from when the user was created',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.customerIo",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication", "Development"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/customerio/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.customerio/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { campaignFields, campaignOperations } from './CampaignDescription';
|
||||
import { customerFields, customerOperations } from './CustomerDescription';
|
||||
import { eventFields, eventOperations } from './EventDescription';
|
||||
import { customerIoApiRequest, validateJSON } from './GenericFunctions';
|
||||
import { segmentFields, segmentOperations } from './SegmentDescription';
|
||||
|
||||
export class CustomerIo implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Customer.io',
|
||||
name: 'customerIo',
|
||||
icon: { light: 'file:customerio.svg', dark: 'file:customerio.dark.svg' },
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Customer.io API',
|
||||
defaults: {
|
||||
name: 'Customer.io',
|
||||
},
|
||||
usableAsTool: true,
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'customerIoApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Customer',
|
||||
value: 'customer',
|
||||
},
|
||||
{
|
||||
name: 'Event',
|
||||
value: 'event',
|
||||
},
|
||||
{
|
||||
name: 'Campaign',
|
||||
value: 'campaign',
|
||||
},
|
||||
{
|
||||
name: 'Segment',
|
||||
value: 'segment',
|
||||
},
|
||||
],
|
||||
default: 'customer',
|
||||
},
|
||||
// CAMPAIGN
|
||||
...campaignOperations,
|
||||
...campaignFields,
|
||||
// CUSTOMER
|
||||
...customerOperations,
|
||||
...customerFields,
|
||||
// EVENT
|
||||
...eventOperations,
|
||||
...eventFields,
|
||||
// SEGMENT
|
||||
...segmentOperations,
|
||||
...segmentFields,
|
||||
],
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const returnData: IDataObject[] = [];
|
||||
const items = this.getInputData();
|
||||
const resource = this.getNodeParameter('resource', 0);
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
const body: IDataObject = {};
|
||||
|
||||
let responseData;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
if (resource === 'campaign') {
|
||||
if (operation === 'get') {
|
||||
const campaignId = this.getNodeParameter('campaignId', i) as number;
|
||||
const endpoint = `/campaigns/${campaignId}`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'GET', endpoint, body, 'beta');
|
||||
responseData = responseData.campaign;
|
||||
}
|
||||
|
||||
if (operation === 'getAll') {
|
||||
const endpoint = '/campaigns';
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'GET', endpoint, body, 'beta');
|
||||
responseData = responseData.campaigns;
|
||||
}
|
||||
|
||||
if (operation === 'getMetrics') {
|
||||
const campaignId = this.getNodeParameter('campaignId', i) as number;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i);
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter(
|
||||
'additionalFieldsJson',
|
||||
i,
|
||||
) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Additional fields must be a valid JSON',
|
||||
{ itemIndex: i },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const period = this.getNodeParameter('period', i) as string;
|
||||
let endpoint = `/campaigns/${campaignId}/metrics`;
|
||||
|
||||
if (period !== 'days') {
|
||||
endpoint = `${endpoint}?period=${period}`;
|
||||
}
|
||||
if (additionalFields.steps) {
|
||||
body.steps = additionalFields.steps as number;
|
||||
}
|
||||
if (additionalFields.type) {
|
||||
if (additionalFields.type === 'urbanAirship') {
|
||||
additionalFields.type = 'urban_airship';
|
||||
} else {
|
||||
body.type = additionalFields.type as string;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'GET', endpoint, body, 'beta');
|
||||
responseData = responseData.metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'customer') {
|
||||
if (operation === 'upsert') {
|
||||
const id = this.getNodeParameter('id', i) as number;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i);
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter(
|
||||
'additionalFieldsJson',
|
||||
i,
|
||||
) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Additional fields must be a valid JSON',
|
||||
{ itemIndex: i },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
|
||||
if (additionalFields.customProperties) {
|
||||
const data: any = {};
|
||||
//@ts-ignore
|
||||
additionalFields.customProperties.customProperty.map((property) => {
|
||||
data[property.key] = property.value;
|
||||
});
|
||||
|
||||
body.data = data;
|
||||
}
|
||||
|
||||
if (additionalFields.email) {
|
||||
body.email = additionalFields.email as string;
|
||||
}
|
||||
|
||||
if (additionalFields.createdAt) {
|
||||
body.created_at = new Date(additionalFields.createdAt as string).getTime() / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `/customers/${id}`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'PUT', endpoint, body, 'tracking');
|
||||
|
||||
responseData = Object.assign({ id }, body);
|
||||
}
|
||||
|
||||
if (operation === 'delete') {
|
||||
const id = this.getNodeParameter('id', i) as number;
|
||||
|
||||
body.id = id;
|
||||
|
||||
const endpoint = `/customers/${id}`;
|
||||
|
||||
await customerIoApiRequest.call(this, 'DELETE', endpoint, body, 'tracking');
|
||||
|
||||
responseData = {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'event') {
|
||||
if (operation === 'track') {
|
||||
const customerId = this.getNodeParameter('customerId', i) as number;
|
||||
const eventName = this.getNodeParameter('eventName', i) as string;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i);
|
||||
|
||||
body.name = eventName;
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter(
|
||||
'additionalFieldsJson',
|
||||
i,
|
||||
) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Additional fields must be a valid JSON',
|
||||
{ itemIndex: i },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const data: any = {};
|
||||
|
||||
if (additionalFields.customAttributes) {
|
||||
//@ts-ignore
|
||||
additionalFields.customAttributes.customAttribute.map((property) => {
|
||||
data[property.key] = property.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (additionalFields.type) {
|
||||
data.type = additionalFields.type as string;
|
||||
}
|
||||
|
||||
body.data = data;
|
||||
}
|
||||
|
||||
const endpoint = `/customers/${customerId}/events`;
|
||||
|
||||
await customerIoApiRequest.call(this, 'POST', endpoint, body, 'tracking');
|
||||
responseData = {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (operation === 'trackAnonymous') {
|
||||
const eventName = this.getNodeParameter('eventName', i) as string;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i);
|
||||
|
||||
body.name = eventName;
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter(
|
||||
'additionalFieldsJson',
|
||||
i,
|
||||
) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Additional fields must be a valid JSON',
|
||||
{ itemIndex: i },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const data: any = {};
|
||||
|
||||
if (additionalFields.customAttributes) {
|
||||
//@ts-ignore
|
||||
additionalFields.customAttributes.customAttribute.map((property) => {
|
||||
data[property.key] = property.value;
|
||||
});
|
||||
}
|
||||
body.data = data;
|
||||
}
|
||||
|
||||
const endpoint = '/events';
|
||||
await customerIoApiRequest.call(this, 'POST', endpoint, body, 'tracking');
|
||||
|
||||
responseData = {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'segment') {
|
||||
const segmentId = this.getNodeParameter('segmentId', i) as number;
|
||||
const customerIds = this.getNodeParameter('customerIds', i) as string;
|
||||
|
||||
body.id = segmentId;
|
||||
body.ids = customerIds.split(',');
|
||||
|
||||
let endpoint = '';
|
||||
|
||||
if (operation === 'add') {
|
||||
endpoint = `/segments/${segmentId}/add_customers`;
|
||||
} else {
|
||||
endpoint = `/segments/${segmentId}/remove_customers`;
|
||||
}
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'POST', endpoint, body, 'tracking');
|
||||
|
||||
responseData = {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
} else {
|
||||
returnData.push(responseData as unknown as IDataObject);
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.customerIoTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication", "Development"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/customerio/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.customeriotrigger/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
import type {
|
||||
IHookFunctions,
|
||||
IWebhookFunctions,
|
||||
IDataObject,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IWebhookResponseData,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionTypes } from 'n8n-workflow';
|
||||
|
||||
import { customerIoApiRequest, eventExists } from './GenericFunctions';
|
||||
|
||||
interface IEvent {
|
||||
customer?: IDataObject;
|
||||
email?: IDataObject;
|
||||
push?: IDataObject;
|
||||
slack?: IDataObject;
|
||||
sms?: IDataObject;
|
||||
webhook?: IDataObject;
|
||||
}
|
||||
|
||||
export class CustomerIoTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Customer.io Trigger',
|
||||
name: 'customerIoTrigger',
|
||||
group: ['trigger'],
|
||||
icon: { light: 'file:customerio.svg', dark: 'file:customerio.dark.svg' },
|
||||
version: 1,
|
||||
description: 'Starts the workflow on a Customer.io update (Beta)',
|
||||
defaults: {
|
||||
name: 'Customer.io Trigger',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'customerIoApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Events',
|
||||
name: 'events',
|
||||
type: 'multiOptions',
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The events that can trigger the webhook and whether they are enabled',
|
||||
options: [
|
||||
{
|
||||
name: 'Customer Subscribed',
|
||||
value: 'customer.subscribed',
|
||||
description: 'Whether the webhook is triggered when a list subscriber is added',
|
||||
},
|
||||
{
|
||||
name: 'Customer Unsubscribe',
|
||||
value: 'customer.unsubscribed',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Attempted',
|
||||
value: 'email.attempted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Bounced',
|
||||
value: 'email.bounced',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Clicked',
|
||||
value: 'email.clicked',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Converted',
|
||||
value: 'email.converted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Delivered',
|
||||
value: 'email.delivered',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Drafted',
|
||||
value: 'email.drafted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Failed',
|
||||
value: 'email.failed',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Opened',
|
||||
value: 'email.opened',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Sent',
|
||||
value: 'email.sent',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Email Spammed',
|
||||
value: 'email.spammed',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Attempted',
|
||||
value: 'push.attempted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Bounced',
|
||||
value: 'push.bounced',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Clicked',
|
||||
value: 'push.clicked',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Delivered',
|
||||
value: 'push.delivered',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Drafted',
|
||||
value: 'push.drafted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Failed',
|
||||
value: 'push.failed',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Opened',
|
||||
value: 'push.opened',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Push Sent',
|
||||
value: 'push.sent',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Slack Attempted',
|
||||
value: 'slack.attempted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Slack Clicked',
|
||||
value: 'slack.clicked',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Slack Drafted',
|
||||
value: 'slack.drafted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Slack Failed',
|
||||
value: 'slack.failed',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'Slack Sent',
|
||||
value: 'slack.sent',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'SMS Attempted',
|
||||
value: 'sms.attempted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'SMS Bounced',
|
||||
value: 'sms.bounced',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'SMS Clicked',
|
||||
value: 'sms.clicked',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'SMS Delivered',
|
||||
value: 'sms.delivered',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'SMS Drafted',
|
||||
value: 'sms.drafted',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'SMS Failed',
|
||||
value: 'sms.failed',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
{
|
||||
name: 'SMS Sent',
|
||||
value: 'sms.sent',
|
||||
description: 'Whether the webhook is triggered when a list member unsubscribes',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
|
||||
const currentEvents = this.getNodeParameter('events', []) as string[];
|
||||
|
||||
const endpoint = '/reporting_webhooks';
|
||||
|
||||
let { reporting_webhooks: webhooks } = await customerIoApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
endpoint,
|
||||
{},
|
||||
'beta',
|
||||
);
|
||||
|
||||
if (webhooks === null) {
|
||||
webhooks = [];
|
||||
}
|
||||
|
||||
for (const webhook of webhooks) {
|
||||
if (
|
||||
webhook.endpoint === webhookUrl &&
|
||||
eventExists(currentEvents, webhook.events as IDataObject)
|
||||
) {
|
||||
webhookData.webhookId = webhook.id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const events = this.getNodeParameter('events', []) as string[];
|
||||
|
||||
const endpoint = '/reporting_webhooks';
|
||||
|
||||
const data: IEvent = {
|
||||
customer: {},
|
||||
email: {},
|
||||
push: {},
|
||||
slack: {},
|
||||
sms: {},
|
||||
webhook: {},
|
||||
};
|
||||
|
||||
for (const event of events) {
|
||||
const option = event.split('.')[1];
|
||||
if (event.startsWith('customer')) {
|
||||
data.customer![option] = true;
|
||||
}
|
||||
if (event.startsWith('email')) {
|
||||
data.email![option] = true;
|
||||
}
|
||||
if (event.startsWith('push')) {
|
||||
data.push![option] = true;
|
||||
}
|
||||
if (event.startsWith('slack')) {
|
||||
data.slack![option] = true;
|
||||
}
|
||||
if (event.startsWith('sms')) {
|
||||
data.sms![option] = true;
|
||||
}
|
||||
if (event.startsWith('webhook')) {
|
||||
data.webhook![option] = true;
|
||||
}
|
||||
}
|
||||
const body = {
|
||||
endpoint: webhookUrl,
|
||||
events: data,
|
||||
};
|
||||
|
||||
const webhook = await customerIoApiRequest.call(this, 'POST', endpoint, body, 'beta');
|
||||
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
webhookData.webhookId = webhook.id as string;
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
|
||||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/reporting_webhooks/${webhookData.webhookId}`;
|
||||
try {
|
||||
await customerIoApiRequest.call(this, 'DELETE', endpoint, {}, 'beta');
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
delete webhookData.webhookId;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const bodyData = this.getBodyData();
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(bodyData)],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const eventOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Track',
|
||||
value: 'track',
|
||||
description: 'Track a customer event',
|
||||
action: 'Track a customer event',
|
||||
},
|
||||
{
|
||||
name: 'Track Anonymous',
|
||||
value: 'trackAnonymous',
|
||||
description: 'Track an anonymous event',
|
||||
action: 'Track an anonymous event',
|
||||
},
|
||||
],
|
||||
default: 'track',
|
||||
},
|
||||
];
|
||||
|
||||
export const eventFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* event:track */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Customer ID',
|
||||
name: 'customerId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['track'],
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer',
|
||||
},
|
||||
{
|
||||
displayName: 'Event Name',
|
||||
name: 'eventName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['track'],
|
||||
},
|
||||
},
|
||||
description: 'Name of the event to track',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['track'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['track'],
|
||||
jsonParameters: [true],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Object of values to set as described <a href="https://customer.io/docs/api-triggered-data-format#basic-data-formatting">here</a>',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['track'],
|
||||
jsonParameters: [false],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributes',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
description: 'Custom Properties',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attribute',
|
||||
name: 'customAttribute',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute name',
|
||||
placeholder: 'Price',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute value',
|
||||
placeholder: '25.50',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Used to change event type. For Page View events set to "page".',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* event:track anonymous */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Event Name',
|
||||
name: 'eventName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['trackAnonymous'],
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['trackAnonymous'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['trackAnonymous'],
|
||||
jsonParameters: [true],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Object of values to set as described <a href="https://customer.io/docs/api-triggered-data-format#basic-data-formatting">here</a>',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['trackAnonymous'],
|
||||
jsonParameters: [false],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributes',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
description: 'Custom Properties',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attribute',
|
||||
name: 'customAttribute',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute name',
|
||||
placeholder: 'Price',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute value',
|
||||
placeholder: '25.50',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,65 @@
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IDataObject,
|
||||
IHttpRequestMethods,
|
||||
IHttpRequestOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function customerIoApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: object,
|
||||
baseApi?: string,
|
||||
_query?: IDataObject,
|
||||
) {
|
||||
const credentials = await this.getCredentials('customerIoApi');
|
||||
const options: IHttpRequestOptions = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
body,
|
||||
url: '',
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (baseApi === 'tracking') {
|
||||
const region = credentials.region;
|
||||
options.url = `https://${region}/api/v1${endpoint}`;
|
||||
} else if (baseApi === 'api') {
|
||||
const region = credentials.region;
|
||||
// Special handling for EU region
|
||||
if (region === 'track-eu.customer.io') {
|
||||
options.url = `https://api-eu.customer.io/v1/api${endpoint}`;
|
||||
} else {
|
||||
options.url = `https://api.customer.io/v1/api${endpoint}`;
|
||||
}
|
||||
} else if (baseApi === 'beta') {
|
||||
options.url = `https://beta-api.customer.io/v1/api${endpoint}`;
|
||||
}
|
||||
|
||||
return await this.helpers.requestWithAuthentication.call(this, 'customerIoApi', options);
|
||||
}
|
||||
|
||||
export function eventExists(currentEvents: string[], webhookEvents: IDataObject) {
|
||||
for (const currentEvent of currentEvents) {
|
||||
if (get(webhookEvents, [currentEvent.split('.')[0], currentEvent.split('.')[1]]) !== true) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateJSON(json: string | undefined): any {
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const segmentOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['segment'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add Customer',
|
||||
value: 'add',
|
||||
action: 'Add a customer to a segment',
|
||||
},
|
||||
{
|
||||
name: 'Remove Customer',
|
||||
value: 'remove',
|
||||
action: 'Remove a customer from a segment',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
},
|
||||
];
|
||||
|
||||
export const segmentFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* segment:add */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Segment ID',
|
||||
name: 'segmentId',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['segment'],
|
||||
operation: ['add', 'remove'],
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier of the segment',
|
||||
},
|
||||
{
|
||||
displayName: 'Customer IDs',
|
||||
name: 'customerIds',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['segment'],
|
||||
operation: ['add', 'remove'],
|
||||
},
|
||||
},
|
||||
description: 'A list of customer IDs to add to the segment',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.0044 4.95768L9.99002 0H0V9.9538L4.97597 14.9494L9.99002 19.9078L4.97597 24.9041L0 29.8995V39.8533H9.99002L19.9804 29.8995V39.8518H30.0092L40 29.8987V9.9538L30.0092 0H19.9804L19.9811 9.95306L15.0044 4.95768ZM19.9811 9.95306L30.0092 19.907L19.9804 29.8995L9.99002 19.9078L19.9811 9.95306Z" fill="#EAFED3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.0044 4.95768L9.99002 0H0V9.9538L4.97597 14.9494L9.99002 19.9078L4.97597 24.9041L0 29.8995V39.8533H9.99002L19.9804 29.8995V39.8518H30.0092L40 29.8987V9.9538L30.0092 0H19.9804L19.9811 9.95306L15.0044 4.95768ZM19.9811 9.95306L30.0092 19.907L19.9804 29.8995L9.99002 19.9078L19.9811 9.95306Z" fill="#0B353B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
Reference in New Issue
Block a user