n8n-nodes-twenty-pro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +78 -0
  2. package/gulpfile.js +38 -0
  3. package/package.json +55 -0
  4. package/src/credentials/TwentyProApi.credentials.ts +27 -0
  5. package/src/index.ts +2 -0
  6. package/src/nodes/actions/ApiKey/TwentyProApiKey.node.ts +84 -0
  7. package/src/nodes/actions/ApiKey/operations/create.ts +14 -0
  8. package/src/nodes/actions/ApiKey/operations/delete.ts +12 -0
  9. package/src/nodes/actions/ApiKey/operations/get.ts +12 -0
  10. package/src/nodes/actions/ApiKey/operations/getMany.ts +14 -0
  11. package/src/nodes/actions/ApiKey/operations/update.ts +15 -0
  12. package/src/nodes/actions/Attachment/TwentyProAttachment.node.ts +85 -0
  13. package/src/nodes/actions/Attachment/operations/create.ts +14 -0
  14. package/src/nodes/actions/Attachment/operations/delete.ts +12 -0
  15. package/src/nodes/actions/Attachment/operations/get.ts +12 -0
  16. package/src/nodes/actions/Attachment/operations/getMany.ts +14 -0
  17. package/src/nodes/actions/Attachment/operations/update.ts +15 -0
  18. package/src/nodes/actions/CalendarEvent/TwentyProCalendarEvent.node.ts +85 -0
  19. package/src/nodes/actions/CalendarEvent/operations/create.ts +14 -0
  20. package/src/nodes/actions/CalendarEvent/operations/delete.ts +12 -0
  21. package/src/nodes/actions/CalendarEvent/operations/get.ts +12 -0
  22. package/src/nodes/actions/CalendarEvent/operations/getMany.ts +14 -0
  23. package/src/nodes/actions/CalendarEvent/operations/update.ts +15 -0
  24. package/src/nodes/actions/Company/TwentyProCompany.node.ts +111 -0
  25. package/src/nodes/actions/Company/operations/create.ts +14 -0
  26. package/src/nodes/actions/Company/operations/delete.ts +12 -0
  27. package/src/nodes/actions/Company/operations/get.ts +12 -0
  28. package/src/nodes/actions/Company/operations/getMany.ts +14 -0
  29. package/src/nodes/actions/Company/operations/update.ts +15 -0
  30. package/src/nodes/actions/CustomObject/TwentyProCustomObject.node.ts +84 -0
  31. package/src/nodes/actions/CustomObject/operations/create.ts +14 -0
  32. package/src/nodes/actions/CustomObject/operations/delete.ts +12 -0
  33. package/src/nodes/actions/CustomObject/operations/get.ts +12 -0
  34. package/src/nodes/actions/CustomObject/operations/getMany.ts +14 -0
  35. package/src/nodes/actions/CustomObject/operations/update.ts +15 -0
  36. package/src/nodes/actions/Message/TwentyProMessage.node.ts +84 -0
  37. package/src/nodes/actions/Message/operations/create.ts +14 -0
  38. package/src/nodes/actions/Message/operations/delete.ts +12 -0
  39. package/src/nodes/actions/Message/operations/get.ts +12 -0
  40. package/src/nodes/actions/Message/operations/getMany.ts +14 -0
  41. package/src/nodes/actions/Message/operations/update.ts +15 -0
  42. package/src/nodes/actions/Note/TwentyProNote.node.ts +97 -0
  43. package/src/nodes/actions/Note/operations/create.ts +14 -0
  44. package/src/nodes/actions/Note/operations/delete.ts +12 -0
  45. package/src/nodes/actions/Note/operations/get.ts +12 -0
  46. package/src/nodes/actions/Note/operations/getMany.ts +14 -0
  47. package/src/nodes/actions/Note/operations/update.ts +15 -0
  48. package/src/nodes/actions/Opportunity/TwentyProOpportunity.node.ts +89 -0
  49. package/src/nodes/actions/Opportunity/operations/create.ts +14 -0
  50. package/src/nodes/actions/Opportunity/operations/delete.ts +12 -0
  51. package/src/nodes/actions/Opportunity/operations/get.ts +12 -0
  52. package/src/nodes/actions/Opportunity/operations/getMany.ts +14 -0
  53. package/src/nodes/actions/Opportunity/operations/update.ts +15 -0
  54. package/src/nodes/actions/Person/TwentyProPerson.node.ts +93 -0
  55. package/src/nodes/actions/Person/operations/create.ts +14 -0
  56. package/src/nodes/actions/Person/operations/delete.ts +12 -0
  57. package/src/nodes/actions/Person/operations/get.ts +12 -0
  58. package/src/nodes/actions/Person/operations/getMany.ts +14 -0
  59. package/src/nodes/actions/Person/operations/update.ts +15 -0
  60. package/src/nodes/actions/Task/TwentyProTask.node.ts +89 -0
  61. package/src/nodes/actions/Task/operations/create.ts +14 -0
  62. package/src/nodes/actions/Task/operations/delete.ts +12 -0
  63. package/src/nodes/actions/Task/operations/get.ts +12 -0
  64. package/src/nodes/actions/Task/operations/getMany.ts +14 -0
  65. package/src/nodes/actions/Task/operations/update.ts +15 -0
  66. package/src/nodes/actions/View/TwentyProView.node.ts +84 -0
  67. package/src/nodes/actions/View/operations/create.ts +14 -0
  68. package/src/nodes/actions/View/operations/delete.ts +12 -0
  69. package/src/nodes/actions/View/operations/get.ts +12 -0
  70. package/src/nodes/actions/View/operations/getMany.ts +14 -0
  71. package/src/nodes/actions/View/operations/update.ts +15 -0
  72. package/src/nodes/actions/Webhook/TwentyProWebhook.node.ts +84 -0
  73. package/src/nodes/actions/Webhook/operations/create.ts +14 -0
  74. package/src/nodes/actions/Webhook/operations/delete.ts +12 -0
  75. package/src/nodes/actions/Webhook/operations/get.ts +12 -0
  76. package/src/nodes/actions/Webhook/operations/getMany.ts +14 -0
  77. package/src/nodes/actions/Webhook/operations/update.ts +15 -0
  78. package/src/nodes/actions/Workflow/TwentyProWorkflow.node.ts +85 -0
  79. package/src/nodes/actions/Workflow/operations/create.ts +14 -0
  80. package/src/nodes/actions/Workflow/operations/delete.ts +12 -0
  81. package/src/nodes/actions/Workflow/operations/get.ts +12 -0
  82. package/src/nodes/actions/Workflow/operations/getMany.ts +14 -0
  83. package/src/nodes/actions/Workflow/operations/update.ts +15 -0
  84. package/src/nodes/shared/FieldTransformers.ts +245 -0
  85. package/src/nodes/shared/RecordLocator.ts +68 -0
  86. package/src/nodes/shared/SchemaDiscovery.ts +95 -0
  87. package/src/nodes/shared/TwentyProApi.client.ts +156 -0
  88. package/src/nodes/shared/twentyPro.svg +6 -0
  89. package/src/nodes/shared/types.ts +59 -0
  90. package/src/nodes/triggers/ApiKey/TwentyProApiKeyTrigger.node.ts +50 -0
  91. package/src/nodes/triggers/Attachment/TwentyProAttachmentTrigger.node.ts +50 -0
  92. package/src/nodes/triggers/CalendarEvent/TwentyProCalendarEventTrigger.node.ts +50 -0
  93. package/src/nodes/triggers/Company/TwentyProCompanyTrigger.node.ts +50 -0
  94. package/src/nodes/triggers/CustomObject/TwentyProCustomObjectTrigger.node.ts +50 -0
  95. package/src/nodes/triggers/Message/TwentyProMessageTrigger.node.ts +50 -0
  96. package/src/nodes/triggers/Note/TwentyProNoteTrigger.node.ts +50 -0
  97. package/src/nodes/triggers/Opportunity/TwentyProOpportunityTrigger.node.ts +50 -0
  98. package/src/nodes/triggers/Person/TwentyProPersonTrigger.node.ts +50 -0
  99. package/src/nodes/triggers/Task/TwentyProTaskTrigger.node.ts +50 -0
  100. package/src/nodes/triggers/View/TwentyProViewTrigger.node.ts +50 -0
  101. package/src/nodes/triggers/Webhook/TwentyProWebhookTrigger.node.ts +50 -0
  102. package/src/nodes/triggers/Workflow/TwentyProWorkflowTrigger.node.ts +50 -0
  103. package/tsconfig.json +19 -0
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnAll = this.getNode().parameters.returnAll as boolean;
6
+ const limit = this.getNode().parameters.limit as number;
7
+ const qs: Record<string, string> = {};
8
+ if (!returnAll) { qs.limit = String(limit); }
9
+ const response = await apiRequest.call(this, 'GET', '/calendarEvents', undefined, qs) as IDataObject;
10
+ const data = response.data as IDataObject | undefined;
11
+ const dataKeys = data ? Object.keys(data) : [];
12
+ const items_arr = dataKeys.length > 0 ? ((data![dataKeys[0]] as IDataObject[]) ?? []) : [];
13
+ return items_arr.map((item) => ({ json: item }));
14
+ }
@@ -0,0 +1,15 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformCalendarEventFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const id = this.getNode().parameters.recordId as string;
9
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
10
+ const transformed = transformCalendarEventFields(fields);
11
+ const response = await apiRequest.call(this, 'PATCH', `/calendarEvents/${id}`, transformed);
12
+ returnData.push({ json: response as IDataObject });
13
+ }
14
+ return returnData;
15
+ }
@@ -0,0 +1,111 @@
1
+ import { IDataObject, INodeType, INodeTypeDescription, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import * as create from './operations/create';
3
+ import * as get from './operations/get';
4
+ import * as update from './operations/update';
5
+ import * as del from './operations/delete';
6
+ import * as getMany from './operations/getMany';
7
+
8
+ export class TwentyProCompany implements INodeType {
9
+ description: INodeTypeDescription = {
10
+ displayName: 'Twenty Company',
11
+ name: 'twentyProCompany',
12
+ icon: 'file:twentyPro.svg',
13
+ group: ['transform'],
14
+ version: 1,
15
+ subtitle: '={{$parameter["operation"]}}',
16
+ description: 'Manage companies in Twenty CRM',
17
+ defaults: { name: 'Twenty Company' },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [{ name: 'twentyProApi', required: true }],
21
+ properties: [
22
+ {
23
+ displayName: 'Operation',
24
+ name: 'operation',
25
+ type: 'options',
26
+ noDataExpression: true,
27
+ required: true,
28
+ default: 'create',
29
+ options: [
30
+ { name: 'Create', value: 'create', description: 'Create a new company' },
31
+ { name: 'Get', value: 'get', description: 'Get a company by ID' },
32
+ { name: 'Update', value: 'update', description: 'Update a company' },
33
+ { name: 'Delete', value: 'delete', description: 'Delete a company' },
34
+ { name: 'Get Many', value: 'getMany', description: 'List companies' },
35
+ ],
36
+ },
37
+ // Record ID field (for get, update, delete)
38
+ {
39
+ displayName: 'Record ID',
40
+ name: 'recordId',
41
+ type: 'string',
42
+ default: '',
43
+ required: true,
44
+ displayOptions: { show: { operation: ['get', 'update', 'delete'] } },
45
+ placeholder: 'abc-123-def-456',
46
+ },
47
+ // Fields (for create, update)
48
+ {
49
+ displayName: 'Fields',
50
+ name: 'fields',
51
+ placeholder: 'Add Field',
52
+ type: 'fixedCollection',
53
+ default: {},
54
+ typeOptions: { multipleValues: true },
55
+ displayOptions: { show: { operation: ['create', 'update'] } },
56
+ options: [
57
+ {
58
+ name: 'fieldValues',
59
+ displayName: 'Field',
60
+ values: [
61
+ { displayName: 'Name', name: 'name', type: 'string', default: '', required: true },
62
+ { displayName: 'Domain', name: 'domainName', type: 'string', default: '' },
63
+ { displayName: 'LinkedIn URL', name: 'linkedinUrl', type: 'string', default: '' },
64
+ { displayName: 'X (Twitter) URL', name: 'xUrl', type: 'string', default: '' },
65
+ {
66
+ displayName: 'Address',
67
+ name: 'address',
68
+ type: 'fixedCollection',
69
+ default: {},
70
+ options: [
71
+ {
72
+ name: 'addressValues',
73
+ displayName: 'Address',
74
+ values: [
75
+ { displayName: 'Street 1', name: 'street1', type: 'string', default: '' },
76
+ { displayName: 'Street 2', name: 'street2', type: 'string', default: '' },
77
+ { displayName: 'City', name: 'city', type: 'string', default: '' },
78
+ { displayName: 'State', name: 'state', type: 'string', default: '' },
79
+ { displayName: 'Postcode', name: 'postcode', type: 'string', default: '' },
80
+ { displayName: 'Country', name: 'country', type: 'string', default: '' },
81
+ ],
82
+ },
83
+ ],
84
+ },
85
+ { displayName: 'Annual Recurring Revenue', name: 'annualRecurringRevenue', type: 'number', default: 0 },
86
+ { displayName: 'Employees', name: 'employees', type: 'number', default: 0 },
87
+ ],
88
+ },
89
+ ],
90
+ },
91
+ // Return All / Limit (for getMany)
92
+ { displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false, displayOptions: { show: { operation: ['getMany'] } } },
93
+ { displayName: 'Limit', name: 'limit', type: 'number', default: 50, displayOptions: { show: { operation: ['getMany'], returnAll: [false] } }, typeOptions: { minValue: 1, maxValue: 100 } },
94
+ ],
95
+ };
96
+
97
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
98
+ const items = this.getInputData();
99
+ const operation = this.getNode().parameters.operation as string;
100
+ let result: INodeExecutionData[];
101
+ switch (operation) {
102
+ case 'create': result = await create.execute.call(this, items); break;
103
+ case 'get': result = await get.execute.call(this, items); break;
104
+ case 'update': result = await update.execute.call(this, items); break;
105
+ case 'delete': result = await del.execute.call(this, items); break;
106
+ case 'getMany': result = await getMany.execute.call(this, items); break;
107
+ default: throw new Error(`Unknown operation: ${operation}`);
108
+ }
109
+ return [result];
110
+ }
111
+ }
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformCompanyFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
9
+ const transformed = transformCompanyFields(fields);
10
+ const response = await apiRequest.call(this, 'POST', '/companies', transformed);
11
+ returnData.push({ json: response as IDataObject });
12
+ }
13
+ return returnData;
14
+ }
@@ -0,0 +1,12 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnData: INodeExecutionData[] = [];
6
+ for (let i = 0; i < items.length; i++) {
7
+ const id = this.getNode().parameters.recordId as string;
8
+ await apiRequest.call(this, 'DELETE', `/companies/${id}`);
9
+ returnData.push({ json: { success: true, id } as IDataObject });
10
+ }
11
+ return returnData;
12
+ }
@@ -0,0 +1,12 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnData: INodeExecutionData[] = [];
6
+ for (let i = 0; i < items.length; i++) {
7
+ const id = this.getNode().parameters.recordId as string;
8
+ const response = await apiRequest.call(this, 'GET', `/companies/${id}`);
9
+ returnData.push({ json: response as IDataObject });
10
+ }
11
+ return returnData;
12
+ }
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnAll = this.getNode().parameters.returnAll as boolean;
6
+ const limit = this.getNode().parameters.limit as number;
7
+ const qs: Record<string, string> = {};
8
+ if (!returnAll) { qs.limit = String(limit); }
9
+ const response = await apiRequest.call(this, 'GET', '/companies', undefined, qs) as IDataObject;
10
+ const data = response.data as IDataObject | undefined;
11
+ const dataKeys = data ? Object.keys(data) : [];
12
+ const items_arr = dataKeys.length > 0 ? ((data![dataKeys[0]] as IDataObject[]) ?? []) : [];
13
+ return items_arr.map((item) => ({ json: item }));
14
+ }
@@ -0,0 +1,15 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformCompanyFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const id = this.getNode().parameters.recordId as string;
9
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
10
+ const transformed = transformCompanyFields(fields);
11
+ const response = await apiRequest.call(this, 'PATCH', `/companies/${id}`, transformed);
12
+ returnData.push({ json: response as IDataObject });
13
+ }
14
+ return returnData;
15
+ }
@@ -0,0 +1,84 @@
1
+ import { IDataObject, INodeType, INodeTypeDescription, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import * as create from './operations/create';
3
+ import * as get from './operations/get';
4
+ import * as update from './operations/update';
5
+ import * as del from './operations/delete';
6
+ import * as getMany from './operations/getMany';
7
+
8
+ export class TwentyProCustomObject implements INodeType {
9
+ description: INodeTypeDescription = {
10
+ displayName: 'Twenty Custom Object',
11
+ name: 'twentyProCustomObject',
12
+ icon: 'file:twentyPro.svg',
13
+ group: ['transform'],
14
+ version: 1,
15
+ subtitle: '={{$parameter["operation"]}}',
16
+ description: 'Manage custom objects in Twenty CRM',
17
+ defaults: { name: 'Twenty Custom Object' },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [{ name: 'twentyProApi', required: true }],
21
+ properties: [
22
+ {
23
+ displayName: 'Operation',
24
+ name: 'operation',
25
+ type: 'options',
26
+ noDataExpression: true,
27
+ required: true,
28
+ default: 'create',
29
+ options: [
30
+ { name: 'Create', value: 'create', description: 'Create a new custom object' },
31
+ { name: 'Get', value: 'get', description: 'Get a custom object by ID' },
32
+ { name: 'Update', value: 'update', description: 'Update a custom object' },
33
+ { name: 'Delete', value: 'delete', description: 'Delete a custom object' },
34
+ { name: 'Get Many', value: 'getMany', description: 'List custom objects' },
35
+ ],
36
+ },
37
+ {
38
+ displayName: 'Record ID',
39
+ name: 'recordId',
40
+ type: 'string',
41
+ default: '',
42
+ required: true,
43
+ displayOptions: { show: { operation: ['get', 'update', 'delete'] } },
44
+ placeholder: 'abc-123-def-456',
45
+ },
46
+ {
47
+ displayName: 'Fields',
48
+ name: 'fields',
49
+ placeholder: 'Add Field',
50
+ type: 'fixedCollection',
51
+ default: {},
52
+ typeOptions: { multipleValues: true },
53
+ displayOptions: { show: { operation: ['create', 'update'] } },
54
+ options: [
55
+ {
56
+ name: 'fieldValues',
57
+ displayName: 'Field',
58
+ values: [
59
+ { displayName: 'Name', name: 'name', type: 'string', default: '' },
60
+ { displayName: 'Fields', name: 'fields', type: 'string', default: '{}' },
61
+ ],
62
+ },
63
+ ],
64
+ },
65
+ { displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false, displayOptions: { show: { operation: ['getMany'] } } },
66
+ { displayName: 'Limit', name: 'limit', type: 'number', default: 50, displayOptions: { show: { operation: ['getMany'], returnAll: [false] } }, typeOptions: { minValue: 1, maxValue: 100 } },
67
+ ],
68
+ };
69
+
70
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
71
+ const items = this.getInputData();
72
+ const operation = this.getNode().parameters.operation as string;
73
+ let result: INodeExecutionData[];
74
+ switch (operation) {
75
+ case 'create': result = await create.execute.call(this, items); break;
76
+ case 'get': result = await get.execute.call(this, items); break;
77
+ case 'update': result = await update.execute.call(this, items); break;
78
+ case 'delete': result = await del.execute.call(this, items); break;
79
+ case 'getMany': result = await getMany.execute.call(this, items); break;
80
+ default: throw new Error(`Unknown operation: ${operation}`);
81
+ }
82
+ return [result];
83
+ }
84
+ }
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformCustomObjectFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
9
+ const transformed = transformCustomObjectFields(fields);
10
+ const response = await apiRequest.call(this, 'POST', '/customObjects', transformed);
11
+ returnData.push({ json: response as IDataObject });
12
+ }
13
+ return returnData;
14
+ }
@@ -0,0 +1,12 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnData: INodeExecutionData[] = [];
6
+ for (let i = 0; i < items.length; i++) {
7
+ const id = this.getNode().parameters.recordId as string;
8
+ await apiRequest.call(this, 'DELETE', `/customObjects/${id}`);
9
+ returnData.push({ json: { success: true, id } as IDataObject });
10
+ }
11
+ return returnData;
12
+ }
@@ -0,0 +1,12 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnData: INodeExecutionData[] = [];
6
+ for (let i = 0; i < items.length; i++) {
7
+ const id = this.getNode().parameters.recordId as string;
8
+ const response = await apiRequest.call(this, 'GET', `/customObjects/${id}`);
9
+ returnData.push({ json: response as IDataObject });
10
+ }
11
+ return returnData;
12
+ }
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnAll = this.getNode().parameters.returnAll as boolean;
6
+ const limit = this.getNode().parameters.limit as number;
7
+ const qs: Record<string, string> = {};
8
+ if (!returnAll) { qs.limit = String(limit); }
9
+ const response = await apiRequest.call(this, 'GET', '/customObjects', undefined, qs) as IDataObject;
10
+ const data = response.data as IDataObject | undefined;
11
+ const dataKeys = data ? Object.keys(data) : [];
12
+ const items_arr = dataKeys.length > 0 ? ((data![dataKeys[0]] as IDataObject[]) ?? []) : [];
13
+ return items_arr.map((item) => ({ json: item }));
14
+ }
@@ -0,0 +1,15 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformCustomObjectFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const id = this.getNode().parameters.recordId as string;
9
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
10
+ const transformed = transformCustomObjectFields(fields);
11
+ const response = await apiRequest.call(this, 'PATCH', `/customObjects/${id}`, transformed);
12
+ returnData.push({ json: response as IDataObject });
13
+ }
14
+ return returnData;
15
+ }
@@ -0,0 +1,84 @@
1
+ import { IDataObject, INodeType, INodeTypeDescription, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import * as create from './operations/create';
3
+ import * as get from './operations/get';
4
+ import * as update from './operations/update';
5
+ import * as del from './operations/delete';
6
+ import * as getMany from './operations/getMany';
7
+
8
+ export class TwentyProMessage implements INodeType {
9
+ description: INodeTypeDescription = {
10
+ displayName: 'Twenty Message',
11
+ name: 'twentyProMessage',
12
+ icon: 'file:twentyPro.svg',
13
+ group: ['transform'],
14
+ version: 1,
15
+ subtitle: '={{$parameter["operation"]}}',
16
+ description: 'Manage messages in Twenty CRM',
17
+ defaults: { name: 'Twenty Message' },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [{ name: 'twentyProApi', required: true }],
21
+ properties: [
22
+ {
23
+ displayName: 'Operation',
24
+ name: 'operation',
25
+ type: 'options',
26
+ noDataExpression: true,
27
+ required: true,
28
+ default: 'create',
29
+ options: [
30
+ { name: 'Create', value: 'create', description: 'Create a new message' },
31
+ { name: 'Get', value: 'get', description: 'Get a message by ID' },
32
+ { name: 'Update', value: 'update', description: 'Update a message' },
33
+ { name: 'Delete', value: 'delete', description: 'Delete a message' },
34
+ { name: 'Get Many', value: 'getMany', description: 'List messages' },
35
+ ],
36
+ },
37
+ {
38
+ displayName: 'Record ID',
39
+ name: 'recordId',
40
+ type: 'string',
41
+ default: '',
42
+ required: true,
43
+ displayOptions: { show: { operation: ['get', 'update', 'delete'] } },
44
+ placeholder: 'abc-123-def-456',
45
+ },
46
+ {
47
+ displayName: 'Fields',
48
+ name: 'fields',
49
+ placeholder: 'Add Field',
50
+ type: 'fixedCollection',
51
+ default: {},
52
+ typeOptions: { multipleValues: true },
53
+ displayOptions: { show: { operation: ['create', 'update'] } },
54
+ options: [
55
+ {
56
+ name: 'fieldValues',
57
+ displayName: 'Field',
58
+ values: [
59
+ { displayName: 'Subject', name: 'subject', type: 'string', default: '' },
60
+ { displayName: 'Body', name: 'body', type: 'string', default: '' },
61
+ ],
62
+ },
63
+ ],
64
+ },
65
+ { displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false, displayOptions: { show: { operation: ['getMany'] } } },
66
+ { displayName: 'Limit', name: 'limit', type: 'number', default: 50, displayOptions: { show: { operation: ['getMany'], returnAll: [false] } }, typeOptions: { minValue: 1, maxValue: 100 } },
67
+ ],
68
+ };
69
+
70
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
71
+ const items = this.getInputData();
72
+ const operation = this.getNode().parameters.operation as string;
73
+ let result: INodeExecutionData[];
74
+ switch (operation) {
75
+ case 'create': result = await create.execute.call(this, items); break;
76
+ case 'get': result = await get.execute.call(this, items); break;
77
+ case 'update': result = await update.execute.call(this, items); break;
78
+ case 'delete': result = await del.execute.call(this, items); break;
79
+ case 'getMany': result = await getMany.execute.call(this, items); break;
80
+ default: throw new Error(`Unknown operation: ${operation}`);
81
+ }
82
+ return [result];
83
+ }
84
+ }
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformMessageFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
9
+ const transformed = transformMessageFields(fields);
10
+ const response = await apiRequest.call(this, 'POST', '/messages', transformed);
11
+ returnData.push({ json: response as IDataObject });
12
+ }
13
+ return returnData;
14
+ }
@@ -0,0 +1,12 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnData: INodeExecutionData[] = [];
6
+ for (let i = 0; i < items.length; i++) {
7
+ const id = this.getNode().parameters.recordId as string;
8
+ await apiRequest.call(this, 'DELETE', `/messages/${id}`);
9
+ returnData.push({ json: { success: true, id } as IDataObject });
10
+ }
11
+ return returnData;
12
+ }
@@ -0,0 +1,12 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnData: INodeExecutionData[] = [];
6
+ for (let i = 0; i < items.length; i++) {
7
+ const id = this.getNode().parameters.recordId as string;
8
+ const response = await apiRequest.call(this, 'GET', `/messages/${id}`);
9
+ returnData.push({ json: response as IDataObject });
10
+ }
11
+ return returnData;
12
+ }
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+
4
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
5
+ const returnAll = this.getNode().parameters.returnAll as boolean;
6
+ const limit = this.getNode().parameters.limit as number;
7
+ const qs: Record<string, string> = {};
8
+ if (!returnAll) { qs.limit = String(limit); }
9
+ const response = await apiRequest.call(this, 'GET', '/messages', undefined, qs) as IDataObject;
10
+ const data = response.data as IDataObject | undefined;
11
+ const dataKeys = data ? Object.keys(data) : [];
12
+ const items_arr = dataKeys.length > 0 ? ((data![dataKeys[0]] as IDataObject[]) ?? []) : [];
13
+ return items_arr.map((item) => ({ json: item }));
14
+ }
@@ -0,0 +1,15 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformMessageFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const id = this.getNode().parameters.recordId as string;
9
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
10
+ const transformed = transformMessageFields(fields);
11
+ const response = await apiRequest.call(this, 'PATCH', `/messages/${id}`, transformed);
12
+ returnData.push({ json: response as IDataObject });
13
+ }
14
+ return returnData;
15
+ }
@@ -0,0 +1,97 @@
1
+ import { IDataObject, INodeType, INodeTypeDescription, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import * as create from './operations/create';
3
+ import * as get from './operations/get';
4
+ import * as update from './operations/update';
5
+ import * as del from './operations/delete';
6
+ import * as getMany from './operations/getMany';
7
+
8
+ export class TwentyProNote implements INodeType {
9
+ description: INodeTypeDescription = {
10
+ displayName: 'Twenty Note',
11
+ name: 'twentyProNote',
12
+ icon: 'file:twentyPro.svg',
13
+ group: ['transform'],
14
+ version: 1,
15
+ subtitle: '={{$parameter["operation"]}}',
16
+ description: 'Manage notes in Twenty CRM',
17
+ defaults: { name: 'Twenty Note' },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [{ name: 'twentyProApi', required: true }],
21
+ properties: [
22
+ {
23
+ displayName: 'Operation',
24
+ name: 'operation',
25
+ type: 'options',
26
+ noDataExpression: true,
27
+ required: true,
28
+ default: 'create',
29
+ options: [
30
+ { name: 'Create', value: 'create', description: 'Create a new note' },
31
+ { name: 'Get', value: 'get', description: 'Get a note by ID' },
32
+ { name: 'Update', value: 'update', description: 'Update a note' },
33
+ { name: 'Delete', value: 'delete', description: 'Delete a note' },
34
+ { name: 'Get Many', value: 'getMany', description: 'List notes' },
35
+ ],
36
+ },
37
+ {
38
+ displayName: 'Record ID',
39
+ name: 'recordId',
40
+ type: 'string',
41
+ default: '',
42
+ required: true,
43
+ displayOptions: { show: { operation: ['get', 'update', 'delete'] } },
44
+ placeholder: 'abc-123-def-456',
45
+ },
46
+ {
47
+ displayName: 'Fields',
48
+ name: 'fields',
49
+ placeholder: 'Add Field',
50
+ type: 'fixedCollection',
51
+ default: {},
52
+ typeOptions: { multipleValues: true },
53
+ displayOptions: { show: { operation: ['create', 'update'] } },
54
+ options: [
55
+ {
56
+ name: 'fieldValues',
57
+ displayName: 'Field',
58
+ values: [
59
+ { displayName: 'Title', name: 'title', type: 'string', default: '', required: true, description: 'The title of the note' },
60
+ { displayName: 'Content', name: 'body', type: 'string', default: '', description: 'The content of the note' },
61
+ ],
62
+ },
63
+ ],
64
+ },
65
+ {
66
+ displayName: 'Return All',
67
+ name: 'returnAll',
68
+ type: 'boolean',
69
+ default: false,
70
+ displayOptions: { show: { operation: ['getMany'] } },
71
+ },
72
+ {
73
+ displayName: 'Limit',
74
+ name: 'limit',
75
+ type: 'number',
76
+ default: 50,
77
+ displayOptions: { show: { operation: ['getMany'], returnAll: [false] } },
78
+ typeOptions: { minValue: 1, maxValue: 100 },
79
+ },
80
+ ],
81
+ };
82
+
83
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
84
+ const items = this.getInputData();
85
+ const operation = this.getNode().parameters.operation as string;
86
+ let result: INodeExecutionData[];
87
+ switch (operation) {
88
+ case 'create': result = await create.execute.call(this, items); break;
89
+ case 'get': result = await get.execute.call(this, items); break;
90
+ case 'update': result = await update.execute.call(this, items); break;
91
+ case 'delete': result = await del.execute.call(this, items); break;
92
+ case 'getMany': result = await getMany.execute.call(this, items); break;
93
+ default: throw new Error(`Unknown operation: ${operation}`);
94
+ }
95
+ return [result];
96
+ }
97
+ }
@@ -0,0 +1,14 @@
1
+ import { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
2
+ import { apiRequest } from '../../../shared/TwentyProApi.client';
3
+ import { transformNoteFields } from '../../../shared/FieldTransformers';
4
+
5
+ export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
6
+ const returnData: INodeExecutionData[] = [];
7
+ for (let i = 0; i < items.length; i++) {
8
+ const fields = this.getNode().parameters.fields as Record<string, unknown> || {};
9
+ const transformed = transformNoteFields(fields);
10
+ const response = await apiRequest.call(this, 'POST', '/notes', transformed);
11
+ returnData.push({ json: response as IDataObject });
12
+ }
13
+ return returnData;
14
+ }